2 Copyright (c) 1993 Richard V. Nash.
3 Copyright (c) 2000 Dan Papasian
4 Copyright (C) Andrew Tridgell 2002
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
23 static int player_zero(int p);
25 static int get_empty_slot(void)
29 for (i = 0; i < player_globals.p_num; i++) {
30 if (player_globals.parray[i].status == PLAYER_EMPTY)
34 if (i < player_globals.parray_size) {
35 player_globals.p_num++;
36 ZERO_STRUCT(player_globals.parray[i]);
37 player_globals.parray[i].status = PLAYER_EMPTY;
41 if (player_globals.parray_size >= config_get_int("MAX_PLAYERS", DEFAULT_MAX_PLAYER)) {
42 d_printf("Too many players connected!\n");
46 player_globals.parray_size += 100;
47 player_globals.parray = (struct player *)realloc(player_globals.parray,
48 player_globals.parray_size * sizeof(struct player));
50 player_globals.p_num++;
51 ZERO_STRUCT(player_globals.parray[i]);
52 player_globals.parray[i].status = PLAYER_EMPTY;
58 int new = get_empty_slot();
64 static void ResetStats(struct statistics *ss)
70 ss->rating = config_get_int("DEFAULT_RATING", DEFAULT_RATING);
71 ss->sterr = config_get_int("DEFAULT_RD", DEFAULT_RD);
77 static int player_zero(int p)
79 struct player *pp = &player_globals.parray[p];
83 pp->prompt = config_get("DEFAULT_PROMPT");
86 pp->status = PLAYER_NEW;
88 ResetStats(&pp->s_stats);
89 ResetStats(&pp->b_stats);
90 ResetStats(&pp->l_stats);
91 ResetStats(&pp->w_stats);
92 ResetStats(&pp->bug_stats);
94 pp->d_time = config_get_int("DEFAULT_TIME", DEFAULT_TIME);
95 pp->d_inc = config_get_int("DEFAULT_INCREMENT", DEFAULT_INCREMENT);
98 pp->language = LANG_DEFAULT;
101 pp->last_channel = -1;
103 pp->Flags = PFLAG_DEFAULT;
108 void player_free(struct player *pp)
117 FREE(pp->emailAddress);
122 FREE(pp->last_opponent);
123 FREE(pp->simul_info);
124 for (i = 0; i < pp->num_plan; i++)
125 FREE(pp->planLines[i]);
126 for (i = 0; i < pp->num_formula; i++)
127 FREE(pp->formulaLines[i]);
128 list_free(pp->lists);
129 for (i = 0; i < pp->numAlias; i++) {
130 FREE(pp->alias_list[i].comm_name);
131 FREE(pp->alias_list[i].alias);
135 int player_clear(int p)
137 player_free(&player_globals.parray[p]);
142 int player_remove(int p)
144 struct player *pp = &player_globals.parray[p];
147 decline_withdraw_offers(p, -1, -1, DO_DECLINE | DO_WITHDRAW);
148 if (pp->simul_info != NULL)
149 /* Player disconnected in middle of simul */
150 for (i = 0; pp->simul_info != NULL
151 && i < pp->simul_info->numBoards; i++)
152 if (pp->simul_info->boards[i] >= 0)
153 game_disconnect(pp->simul_info->boards[i], p);
155 if ((pp->game >=0) && (pIsPlaying(p))) {
156 /* Player disconnected in the middle of a game! */
157 pprintf(pp->opponent, "Your opponent has lost contact or quit.");
158 game_disconnect(pp->game, p);
160 for (i = 0; i < player_globals.p_num; i++) {
161 if (player_globals.parray[i].status == PLAYER_EMPTY)
163 if (player_globals.parray[i].partner == p) {
164 pprintf_prompt (i, "Your partner has disconnected.\n");
165 decline_withdraw_offers(i, -1, PEND_BUGHOUSE, DO_DECLINE | DO_WITHDRAW);
166 player_globals.parray[i].partner = -1;
170 pp->status = PLAYER_EMPTY;
176 write a player file using the new generic marshalling format
178 static int WritePlayerFile_v100(int p)
180 struct player pp = player_globals.parray[p];
184 /* zero any elements we don't want to save */
185 memset(&pp, 0, offsetof(struct player, not_saved_marker));
187 /* marshall it into a string */
188 s = marshall_player(&pp);
190 d_printf("Unable to marshall player structure for %s\n",
195 fp = fopen_p("%s/%c/%s", "w",
197 player_globals.parray[p].login[0],
198 player_globals.parray[p].login);
200 d_printf("CHESSD: Problem opening player file '%s' for write\n",
201 player_globals.parray[p].login);
207 fprintf(fp, "v 100\n%s\n", s);
214 read a player file using the new generic and extensible format
216 static int ReadPlayerFile_v100(FILE *fp, int p)
219 struct player *pp = &player_globals.parray[p];
221 s = fd_load(fileno(fp), NULL);
223 d_printf("Error reading player file for '%s'\n", pp->login);
227 /* skip first line */
228 s2 = strchr(s, '\n');
230 /* the marshaller doesn't save zero elements, but some elements don't
231 default to zero. Make sure they get the right value */
232 memset(&pp->not_saved_marker, 0,
233 sizeof(struct player) - offsetof(struct player, not_saved_marker));
235 if (!s2 || unmarshall_player(pp, s2) != 0) {
236 d_printf("Error unmarshalling player data for '%s'!\n", pp->login);
247 int player_read(int p, char *name)
249 struct player *pp = &player_globals.parray[p];
250 char fname[MAX_FILENAME_SIZE];
251 char line[MAX_LINE_SIZE];
256 pp->login = stolower(strdup(name));
258 sprintf(fname, "%s/%c/%s", PLAYER_DIR, pp->login[0], pp->login);
259 fp = fopen_s(fname, "r");
264 if (!fp) { /* unregistered player */
265 pp->name = stolower(strdup(name));
266 PFlagOFF(p, PFLAG_REG);
270 PFlagON(p, PFLAG_REG); /* lets load the file */
271 fgets(line, MAX_LINE_SIZE, fp); /* ok so which version file? */
273 if (line[0] == 'v') {
274 sscanf(line, "%*c %d", &version);
277 if (version == 9 || version == 100) {
278 /* its the new style */
279 ret = ReadPlayerFile_v100(fp, p);
281 /* fall back to the old style */
282 ret = player_read_old(p, fp, name, fname, version);
283 player_save(p); /* update to the new generic format */
291 int player_save(int p)
293 struct player *pp = &player_globals.parray[p];
295 if (!CheckPFlag(p, PFLAG_REG)) { /* Player not registered */
298 if (pp->name == NULL) { /* fixes a bug if name is null */
299 pprintf(p, "WARNING: Your player file could not be updated, due to corrupt data.\n");
302 if (strcasecmp(pp->login, pp->name)) {
303 pprintf(p, "WARNING: Your player file could not be updated, due to corrupt data.\n");
307 return WritePlayerFile_v100(p);
310 int player_find(int fd)
314 for (i = 0; i < player_globals.p_num; i++) {
315 if (player_globals.parray[i].status == PLAYER_EMPTY)
317 if (player_globals.parray[i].socket == fd)
323 /* incorrectly named */
324 int player_find_bylogin(const char *name)
328 for (i = 0; i < player_globals.p_num; i++) {
329 if ((player_globals.parray[i].status == PLAYER_EMPTY) ||
330 (player_globals.parray[i].status == PLAYER_LOGIN) ||
331 (player_globals.parray[i].status == PLAYER_PASSWORD))
333 if (!player_globals.parray[i].login)
335 if (!strcasecmp(player_globals.parray[i].name, name))
341 /* Now incorrectly named */
342 int player_find_part_login(const char *name)
347 i = player_find_bylogin(name);
350 for (i = 0; i < player_globals.p_num; i++) {
351 if ((player_globals.parray[i].status == PLAYER_EMPTY) ||
352 (player_globals.parray[i].status == PLAYER_LOGIN) ||
353 (player_globals.parray[i].status == PLAYER_PASSWORD))
355 if (!player_globals.parray[i].name)
357 if (!strncasecmp(player_globals.parray[i].name, name, strlen(name))) {
369 int player_censored(int p, int p1)
371 if (in_list(p, L_CENSOR, player_globals.parray[p1].login))
377 /* is p1 on p's notify list? */
378 int player_notified(int p, int p1)
380 struct player *pp = &player_globals.parray[p];
381 if (!CheckPFlag(p1, PFLAG_REG))
384 /* possible bug: p has just arrived! */
388 return (in_list(p, L_NOTIFY, player_globals.parray[p1].login));
391 void player_notify_departure(int p)
392 /* Notify those with notifiedby set on a departure */
394 struct player *pp = &player_globals.parray[p];
397 if (!CheckPFlag(p, PFLAG_REG))
399 for (p1 = 0; p1 < player_globals.p_num; p1++) {
400 if (CheckPFlag(p1, PFLAG_NOTIFYBY) && !player_notified(p1, p)
401 && player_notified(p, p1) && (player_globals.parray[p1].status == PLAYER_PROMPT)) {
403 pprintf(p1, "\nNotification: ");
404 pprintf_highlight(p1, "%s", pp->name);
405 pprintf_prompt(p1, " has departed and isn't on your notify list.\n");
410 int player_notify_present(int p)
411 /* output Your arrival was notified by..... */
412 /* also notify those with notifiedby set if necessary */
414 struct player *pp = &player_globals.parray[p];
418 if (!CheckPFlag(p, PFLAG_REG))
420 for (p1 = 0; p1 < player_globals.p_num; p1++) {
421 if ((player_notified(p, p1)) && (player_globals.parray[p1].status == PLAYER_PROMPT)) {
423 pprintf(p, "Present company includes:");
426 pprintf(p, " %s", player_globals.parray[p1].name);
427 if (CheckPFlag(p1, PFLAG_NOTIFYBY) && (!player_notified(p1, p))
428 && (player_globals.parray[p1].status == PLAYER_PROMPT)) {
430 pprintf(p1, "\nNotification: ");
431 pprintf_highlight(p1, "%s", pp->name);
432 pprintf_prompt(p1, " has arrived and isn't on your notify list.\n");
441 int player_notify(int p, char *note1, char *note2)
442 /* notify those interested that p has arrived/departed */
444 struct player *pp = &player_globals.parray[p];
447 if (!CheckPFlag(p, PFLAG_REG))
449 for (p1 = 0; p1 < player_globals.p_num; p1++) {
450 if ((player_notified(p1, p)) && (player_globals.parray[p1].status == PLAYER_PROMPT)) {
452 pprintf(p1, "\nNotification: ");
453 pprintf_highlight(p1, "%s", pp->name);
454 pprintf_prompt(p1, " has %s.\n", note1);
456 pprintf(p, "Your %s was noted by:", note2);
459 pprintf(p, " %s", player_globals.parray[p1].name);
467 int showstored(int p)
469 struct player *pp = &player_globals.parray[p];
474 char dname[MAX_FILENAME_SIZE];
475 multicol *m = multicol_start(50); /* Limit to 50, should be enough*/
477 sprintf(dname, "%s/%c", ADJOURNED_DIR, pp->login[0]);
478 dirp = opendir(dname);
483 for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
484 if (file_has_pname(dp->d_name, pp->login)) {
485 if (strcmp(file_wplayer(dp->d_name),pp->login) != 0) {
486 p1=player_find_bylogin(file_wplayer(dp->d_name));
488 p1=player_find_bylogin(file_bplayer(dp->d_name));
492 multicol_store(m,player_globals.parray[p1].name);
493 pprintf(p1,"\nNotification: ");
494 pprintf_highlight(p1,"%s",pp->name);
495 pprintf_prompt(p1,", who has an adjourned game with you, has arrived.\n");
502 pprintf(p, "1 player, who has an adjourned game with you, is online:\007");
504 pprintf(p, "\n%d players, who have an adjourned game with you, are online:\007",c);
507 multicol_pprint(m,p,pp->d_width,2);
513 int player_count(int CountAdmins)
518 for (count = 0, i = 0; i < player_globals.p_num; i++) {
519 if ((player_globals.parray[i].status == PLAYER_PROMPT) &&
520 (CountAdmins || !in_list(i, L_ADMIN, player_globals.parray[i].name)))
523 if (count > command_globals.player_high)
524 command_globals.player_high = count;
529 int player_idle(int p)
531 struct player *pp = &player_globals.parray[p];
532 if (pp->status != PLAYER_PROMPT)
533 return time(0) - pp->logon_time;
535 return time(0) - pp->last_command_time;
538 int player_ontime(int p)
540 struct player *pp = &player_globals.parray[p];
541 return time(0) - pp->logon_time;
544 static void write_p_inout(int inout, int p, char *file, int maxlines)
546 struct player *pp = &player_globals.parray[p];
549 fp = fopen_s(file, "a");
552 fprintf(fp, "%d %s %d %d %s\n", inout, pp->name, (int) time(0),
553 BoolCheckPFlag(p, PFLAG_REG),
554 dotQuad(pp->thisHost));
557 truncate_file(file, maxlines);
560 void player_write_login(int p)
562 struct player *pp = &player_globals.parray[p];
563 char fname[MAX_FILENAME_SIZE];
565 if (CheckPFlag(p, PFLAG_REG)) {
566 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0], pp->login, STATS_LOGONS);
567 write_p_inout(P_LOGIN, p, fname, 8);
569 sprintf(fname, "%s/%s", STATS_DIR, STATS_LOGONS);
570 write_p_inout(P_LOGIN, p, fname, 30);
571 /* added complete login/logout log to "logons.log" file */
572 sprintf(fname, "%s/%s", STATS_DIR, "logons.log");
573 write_p_inout(P_LOGIN, p, fname, 0);
576 void player_write_logout(int p)
578 struct player *pp = &player_globals.parray[p];
579 char fname[MAX_FILENAME_SIZE];
581 if (CheckPFlag(p, PFLAG_REG)) {
582 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0], pp->login, STATS_LOGONS);
583 write_p_inout(P_LOGOUT, p, fname, 8);
585 sprintf(fname, "%s/%s", STATS_DIR, STATS_LOGONS);
586 write_p_inout(P_LOGOUT, p, fname, 30);
587 /* added complete login/logout log to "logons.log" file */
588 sprintf(fname, "%s/%s", STATS_DIR, "logons.log");
589 write_p_inout(P_LOGOUT, p, fname, 0);
592 int player_lastdisconnect(int p)
594 struct player *pp = &player_globals.parray[p];
595 char fname[MAX_FILENAME_SIZE];
597 int inout, thetime, registered;
600 char loginName[MAX_LOGIN_NAME];
602 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0], pp->login, STATS_LOGONS);
603 fp = fopen_s(fname, "r");
607 if (fscanf(fp, "%d %s %d %d %s\n", &inout, loginName, &thetime, ®istered, ipstr) != 5) {
608 d_printf( "CHESSD: Error in login info format. %s\n", fname);
612 if (inout == P_LOGOUT)
619 int player_is_observe(int p, int g)
621 struct player *pp = &player_globals.parray[p];
624 for (i = 0; i < pp->num_observe; i++) {
625 if (pp->observe_list[i] == g)
628 if (i == pp->num_observe)
634 int player_add_observe(int p, int g)
636 struct player *pp = &player_globals.parray[p];
637 if (pp->num_observe == MAX_OBSERVE)
639 pp->observe_list[pp->num_observe] = g;
644 int player_remove_observe(int p, int g)
646 struct player *pp = &player_globals.parray[p];
649 for (i = 0; i < pp->num_observe; i++) {
650 if (pp->observe_list[i] == g)
653 if (i == pp->num_observe)
654 return -1; /* Not found! */
655 for (; i < pp->num_observe - 1; i++) {
656 pp->observe_list[i] = pp->observe_list[i + 1];
662 int player_game_ended(int g)
666 for (p = 0; p < player_globals.p_num; p++) {
667 struct player *pp = &player_globals.parray[p];
668 if (pp->status == PLAYER_EMPTY)
670 player_remove_observe(p, g);
672 remove_request(game_globals.garray[g].white, game_globals.garray[g].black, -1);
673 remove_request(game_globals.garray[g].black, game_globals.garray[g].white, -1);
674 player_save(game_globals.garray[g].white); /* Hawk: Added to save finger-info after each
676 player_save(game_globals.garray[g].black);
680 int player_goto_board(int p, int board_num)
682 struct player *pp = &player_globals.parray[p];
683 int start, count = 0, on, g;
685 if (pp->simul_info == NULL)
688 if (board_num < 0 || board_num >= pp->simul_info->numBoards)
690 if (pp->simul_info->boards[board_num] < 0)
692 pp->simul_info->onBoard = board_num;
693 pp->game = pp->simul_info->boards[board_num];
694 pp->opponent = game_globals.garray[pp->game].black;
695 if (pp->simul_info->numBoards == 1)
697 send_board_to(pp->game, p);
699 on = pp->simul_info->onBoard;
701 g = pp->simul_info->boards[on];
704 Bell (game_globals.garray[g].black);
705 pprintf(game_globals.garray[g].black, "\n");
706 pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
707 pprintf_prompt(game_globals.garray[g].black, " is at your board!\n");
708 } else if (count == 1) {
709 Bell (game_globals.garray[g].black);
710 pprintf(game_globals.garray[g].black, "\n");
711 pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
712 pprintf_prompt(game_globals.garray[g].black, " will be at your board NEXT!\n");
714 pprintf(game_globals.garray[g].black, "\n");
715 pprintf_highlight(game_globals.garray[g].black, "%s", pp->name);
716 pprintf_prompt(game_globals.garray[g].black, " is %d boards away.\n", count);
721 if (on >= pp->simul_info->numBoards)
723 } while (start != pp->simul_info->boards[on]);
727 int player_goto_next_board(int p)
729 struct player *pp = &player_globals.parray[p];
734 if (pp->simul_info == NULL)
737 on = pp->simul_info->onBoard;
742 if (on >= pp->simul_info->numBoards)
744 g = pp->simul_info->boards[on];
747 } while (start != on);
749 pprintf(p, "\nMajor Problem! Can't find your next board.\n");
752 return player_goto_board(p, on);
755 int player_goto_prev_board(int p)
757 struct player *pp = &player_globals.parray[p];
762 if (pp->simul_info == NULL)
765 on = pp->simul_info->onBoard;
771 on = (pp->simul_info->numBoards) - 1;
772 g = pp->simul_info->boards[on];
775 } while (start != on);
777 pprintf(p, "\nMajor Problem! Can't find your previous board.\n");
780 return player_goto_board(p, on);
783 int player_goto_simulgame_bynum(int p, int num)
785 struct player *pp = &player_globals.parray[p];
790 if (pp->simul_info == NULL)
793 on = pp->simul_info->onBoard;
797 if (on >= pp->simul_info->numBoards)
799 g = pp->simul_info->boards[on];
802 } while (start != on);
804 pprintf(p, "\nYou aren't playing that game!!\n");
807 return player_goto_board(p, on);
810 int player_num_active_boards(int p)
812 struct player *pp = &player_globals.parray[p];
815 if (pp->simul_info == NULL)
818 if (!pp->simul_info->numBoards)
820 for (i = 0; i < pp->simul_info->numBoards; i++)
821 if (pp->simul_info->boards[i] >= 0)
826 static int player_num_results(int p, int result)
828 struct player *pp = &player_globals.parray[p];
831 return pp->simul_info->num_wins;
833 return pp->simul_info->num_draws;
835 return pp->simul_info->num_losses;
841 static void new_simul_result(struct simul_info_t *sim, int result)
856 int player_simul_over(int p, int g, int result)
858 struct player *pp = &player_globals.parray[p];
859 int on, ong, p1, which;
862 if (pp->simul_info == NULL)
865 for (which = 0; which < pp->simul_info->numBoards; which++) {
866 if (pp->simul_info->boards[which] == g) {
870 if (which == pp->simul_info->numBoards) {
871 pprintf(p, "I can't find that game!\n");
874 pprintf(p, "\nBoard %d has completed.\n", which + 1);
875 on = pp->simul_info->onBoard;
876 ong = pp->simul_info->boards[on];
877 pp->simul_info->boards[which] = -1;
878 new_simul_result(pp->simul_info, result);
879 if (player_num_active_boards(p) == 0) {
880 sprintf(tmp, "\n{Simul (%s vs. %d) is over.}\nResults: %d Wins, %d Losses, %d Draws, %d Aborts\n",
882 pp->simul_info->numBoards,
883 player_num_results(p, RESULT_WIN),
884 player_num_results(p, RESULT_LOSS),
885 player_num_results(p, RESULT_DRAW),
886 player_num_results(p, RESULT_ABORT));
887 for (p1 = 0; p1 < player_globals.p_num; p1++) {
888 if (pp->status != PLAYER_PROMPT)
890 if (!CheckPFlag(p1, PFLAG_GIN) && !player_is_observe(p1, g) && (p1 != p))
892 pprintf_prompt(p1, "%s", tmp);
894 pp->simul_info->numBoards = 0;
895 pprintf_prompt(p, "\nThat was the last board, thanks for playing.\n");
896 free(pp->simul_info);
897 pp->simul_info = NULL;
900 if (ong == g) { /* This game is over */
901 player_goto_next_board(p);
903 player_goto_board(p, pp->simul_info->onBoard);
905 pprintf_prompt(p, "\nThere are %d boards left.\n",
906 player_num_active_boards(p));
910 static void GetMsgFile (int p, char *fName)
912 struct player *pp = &player_globals.parray[p];
913 sprintf(fName, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0],
914 pp->login, STATS_MESSAGES);
917 int player_num_messages(int p)
919 char fname[MAX_FILENAME_SIZE];
921 if (!CheckPFlag(p, PFLAG_REG))
923 GetMsgFile (p, fname);
924 return lines_file(fname);
927 int player_add_message(int top, int fromp, char *message)
929 char fname[MAX_FILENAME_SIZE];
935 if (!CheckPFlag(top, PFLAG_REG))
937 if (!CheckPFlag(fromp, PFLAG_REG))
939 GetMsgFile (top, fname);
940 if ((lines_file(fname) >= MAX_MESSAGES) && (player_globals.parray[top].adminLevel == 0))
942 fp = fopen_s(fname, "a");
945 fprintf(fp, "%s at %s: %s\n", player_globals.parray[fromp].name, strltime(&t), message);
947 pprintf(fromp, "\nThe following message was sent ");
948 if (CheckPFlag(top, PFLAG_MAILMESS)) {
949 sprintf(subj, "FICS message from %s at FICS %s (Do not reply by mail)",
950 player_globals.parray[fromp].name,
951 config_get_tmp("SERVER_HOSTNAME"));
952 sprintf(messbody, "%s at %s: %s\n", player_globals.parray[fromp].name, strltime(&t), message);
953 mail_string_to_user(top, subj, messbody);
954 pprintf(fromp, "(and emailed) ");
956 pprintf(fromp, "to %s: \n %s\n", player_globals.parray[top].name, message);
960 static int player_forward_message(int top, int fromp, char *message)
962 char fname[MAX_FILENAME_SIZE];
967 if (!CheckPFlag(top, PFLAG_REG))
969 if (!CheckPFlag(fromp, PFLAG_REG))
971 GetMsgFile (top, fname);
972 if ((lines_file(fname) >= MAX_MESSAGES) && (player_globals.parray[top].adminLevel == 0))
974 fp = fopen_s(fname, "a");
977 fprintf(fp, "FORWARDED MSG FROM %s : %s", player_globals.parray[fromp].name, message);
979 pprintf(fromp, "\nThe following message was forwarded ");
980 if (CheckPFlag(top, PFLAG_MAILMESS)) {
981 sprintf(subj, "FICS message from %s at FICS %s (Do not reply by mail)",
982 player_globals.parray[fromp].name,
983 config_get_tmp("SERVER_HOSTNAME"));
984 sprintf(messbody, "FORWARDED MSG FROM %s: %s\n", player_globals.parray[fromp].name, message);
985 mail_string_to_user(top, subj, messbody);
986 pprintf(fromp, "(and emailed) ");
988 pprintf(fromp, "to %s: \n %s\n", player_globals.parray[top].name, message);
991 void SaveTextListEntry(textlist **Entry, char *string, int n)
993 *Entry = (textlist *) malloc(sizeof(textlist));
994 (*Entry)->text = strdup(string);
996 (*Entry)->next = NULL;
999 static textlist *ClearTextListEntry(textlist *entry)
1001 textlist *ret = entry->next;
1007 void ClearTextList(textlist *head)
1011 for (cur = head; cur != NULL; cur = ClearTextListEntry(cur));
1014 static int SaveThisMsg (int which, char *line)
1016 char Sender[MAX_LOGIN_NAME];
1019 if (which == 0) return 1;
1021 sscanf (line, "%s", Sender);
1024 return strcmp(Sender, player_globals.parray[p1].name);
1028 return !strcmp(Sender, player_globals.parray[p1].name);
1032 static int LoadMsgs(int p, int which, textlist **Head)
1035 textlist **Cur = Head;
1036 char fName[MAX_FILENAME_SIZE];
1037 char line[MAX_LINE_SIZE];
1041 GetMsgFile (p, fName);
1042 fp = fopen_s(fName, "r");
1047 fgets(line, MAX_LINE_SIZE, fp);
1050 if (SaveThisMsg(which, line)) {
1051 SaveTextListEntry(Cur, line, ++n);
1052 Cur = &(*Cur)->next;
1061 /* start > 0 and end > start (or end = 0) to save messages in range;
1062 start < 0 and end < start (or end = 0) to clear messages in range;
1063 if end = 0, range goes to end of file (not tested yet). */
1064 static int LoadMsgRange(int p, int start, int end, textlist **Head)
1067 char fName[MAX_FILENAME_SIZE];
1068 char line[MAX_LINE_SIZE];
1069 textlist **Cur = Head;
1070 int n=1, nSave=0, nKill=0;
1073 GetMsgFile (p, fName);
1074 fp = fopen (fName, "r");
1076 pprintf (p, "You have no messages.\n");
1079 for (n=1; n <= end || end <= 0; n++) {
1080 fgets (line, MAX_LINE_SIZE, fp);
1083 if ((start < 0 && (n < -start || n > -end)) || (start >= 0 && n >= start)) {
1084 SaveTextListEntry (Cur, line, n);
1085 Cur = &(*Cur)->next;
1093 pprintf (p, "You do not have a message %d.\n", -start);
1097 pprintf (p, "You do not have a message %d.\n", start);
1102 int ForwardMsgRange(char *p1, int p, int start, int end)
1104 /* p1 is player to and p is player from */
1106 char fName[MAX_FILENAME_SIZE];
1107 char line[MAX_LINE_SIZE];
1108 int n=1, top, connected;
1110 if (!FindPlayer(p, p1, &top, &connected))
1112 GetMsgFile (p, fName);
1113 fp = fopen (fName, "r");
1115 pprintf (p, "You have no messages.\n");
1118 for (n=1; n <= end || end <= 0; n++) {
1119 fgets (line, MAX_LINE_SIZE, fp);
1122 if ((start < 0 && (n < -start || n > -end)) || (start >= 0 && n >= start)) {
1123 player_forward_message(top, p, line);
1129 pprintf (p, "You do not have a message %d.\n", -start);
1133 pprintf (p, "You do not have a message %d.\n", start);
1138 static int WriteMsgFile (int p, textlist *Head)
1140 char fName[MAX_FILENAME_SIZE];
1144 GetMsgFile (p, fName);
1145 fp = fopen_s(fName, "w");
1148 for (Cur = Head; Cur != NULL; Cur = Cur->next)
1149 fprintf(fp, "%s", Cur->text);
1154 int ClearMsgsBySender(int p, param_list param)
1156 struct player *pp = &player_globals.parray[p];
1161 if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1164 nFound = LoadMsgs(p, -(p1+1), &Head);
1166 pprintf(p, "You have no messages.\n");
1167 } else if (nFound == 0) {
1168 pprintf(p, "You have no messages from %s.\n", player_globals.parray[p1].name);
1170 if (WriteMsgFile (p, Head))
1171 pprintf(p, "Messages from %s cleared.\n", player_globals.parray[p1].name);
1173 pprintf(p, "Problem writing message file; please contact an admin.\n");
1174 d_printf( "Problem writing message file for %s.\n", pp->name);
1176 ClearTextList(Head);
1183 static void ShowTextList (int p, textlist *Head, int ShowIndex)
1188 for (CurMsg = Head; CurMsg != NULL; CurMsg = CurMsg->next)
1189 pprintf(p, "%2d. %s", CurMsg->index, CurMsg->text);
1192 for (CurMsg = Head; CurMsg != NULL; CurMsg = CurMsg->next)
1193 pprintf(p, "%s", CurMsg->text);
1197 int player_show_messages(int p)
1202 n = LoadMsgs (p, 0, &Head);
1204 pprintf (p, "You have no messages.\n");
1207 pprintf (p, "Messages:\n");
1208 ShowTextList (p, Head, 1);
1209 ClearTextList (Head);
1214 int ShowMsgsBySender(int p, param_list param)
1221 if (!FindPlayer(p, param[0].val.word, &p1, &connected))
1224 if (!CheckPFlag(p1, PFLAG_REG)) {
1225 pprintf(p, "Player \"%s\" is unregistered and cannot send or receive messages.\n",
1226 player_globals.parray[p1].name);
1227 return -1; /* no need to disconnect */
1231 nTo = LoadMsgs(p1, p+1, &Head);
1233 pprintf(p, "%s has no messages from you.\n", player_globals.parray[p1].name);
1235 pprintf(p, "Messages to %s:\n", player_globals.parray[p1].name);
1236 ShowTextList (p, Head, 0);
1237 ClearTextList(Head);
1240 nFrom = LoadMsgs(p, p1+1, &Head);
1242 pprintf(p, "\nYou have no messages from %s.\n", player_globals.parray[p1].name);
1244 pprintf(p, "Messages from %s:\n", player_globals.parray[p1].name);
1245 ShowTextList (p, Head, 1);
1246 ClearTextList(Head);
1250 return (nFrom > 0 || nTo > 0);
1253 int ShowMsgRange (int p, int start, int end)
1258 n = LoadMsgRange (p, start, end, &Head);
1260 ShowTextList (p, Head, 1);
1261 ClearTextList (Head);
1266 int ClrMsgRange (int p, int start, int end)
1271 n = LoadMsgRange (p, -start, -end, &Head);
1273 if (WriteMsgFile (p, Head))
1274 pprintf (p, "Message %d cleared.\n", start);
1276 ClearTextList (Head);
1281 int player_clear_messages(int p)
1283 char fname[MAX_FILENAME_SIZE];
1285 if (!CheckPFlag(p, PFLAG_REG))
1287 GetMsgFile (p, fname);
1292 int player_search(int p, char *name)
1294 * Find player matching the given string. First looks for exact match
1295 * with a logged in player, then an exact match with a registered player,
1296 * then a partial unique match with a logged in player, then a partial
1297 * match with a registered player.
1298 * Returns player number if the player is connected, negative (player number)
1299 * if the player had to be connected, and 0 if no player was found
1304 char pdir[MAX_FILENAME_SIZE];
1306 /* exact match with connected player? */
1307 if ((p1 = player_find_bylogin(name)) >= 0) {
1310 /* exact match with registered player? */
1311 sprintf(pdir, "%s/%c", PLAYER_DIR, name[0]);
1312 count = search_directory(pdir, name, buffer, 1000);
1313 if (count > 0 && !strcmp(name, *buffer)) {
1314 goto ReadPlayerFromFile; /* found an unconnected registered player */
1316 /* partial match with connected player? */
1317 if ((p1 = player_find_part_login(name)) >= 0) {
1319 } else if (p1 == -2) {
1320 /* ambiguous; matches too many connected players. */
1321 pprintf (p, "Ambiguous name '%s'; matches more than one player.\n", name);
1324 /* partial match with registered player? */
1326 pprintf(p, "There is no player matching that name.\n");
1330 pprintf(p, "-- Matches: %d names --", count);
1331 display_directory(p, buffer, count);
1336 if (player_read(p1, *buffer)) {
1338 pprintf(p, "ERROR: a player named %s was expected but not found!\n",
1340 pprintf(p, "Please tell an admin about this incident. Thank you.\n");
1343 return (-p1) - 1; /* negative to indicate player was not
1348 int player_kill(char *name)
1350 char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1352 sprintf(fname, "%s/%c/%s", PLAYER_DIR, name[0], name);
1353 sprintf(fname2, "%s/%c/.rem.%s", PLAYER_DIR, name[0], name);
1354 rename(fname, fname2);
1356 sprintf(fname, "%s/player_data/%c/%s.games", STATS_DIR, name[0], name);
1357 sprintf(fname2, "%s/player_data/%c/.rem.%s.games", STATS_DIR, name[0], name);
1358 rename(fname, fname2);
1359 sprintf(fname, "%s/player_data/%c/%s.comments", STATS_DIR, name[0], name);
1360 sprintf(fname2, "%s/player_data/%c/.rem.%s.comments", STATS_DIR, name[0], name);
1361 rename(fname, fname2);
1363 sprintf(fname, "%s/player_data/%c/%s.logons", STATS_DIR, name[0], name);
1364 sprintf(fname2, "%s/player_data/%c/.rem.%s.logons", STATS_DIR, name[0], name);
1365 rename(fname, fname2);
1366 sprintf(fname, "%s/player_data/%c/%s.messages", STATS_DIR, name[0], name);
1367 sprintf(fname2, "%s/player_data/%c/.rem.%s.messages", STATS_DIR, name[0], name);
1368 rename(fname, fname2);
1372 int player_rename(char *name, char *newname)
1374 char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1376 sprintf(fname, "%s/%c/%s", PLAYER_DIR, name[0], name);
1377 sprintf(fname2, "%s/%c/%s", PLAYER_DIR, newname[0], newname);
1378 rename(fname, fname2);
1379 sprintf(fname, "%s/player_data/%c/%s.games", STATS_DIR, name[0], name);
1380 sprintf(fname2, "%s/player_data/%c/%s.games", STATS_DIR, newname[0], newname);
1381 rename(fname, fname2);
1382 sprintf(fname, "%s/player_data/%c/%s.comments", STATS_DIR, name[0], name);
1383 sprintf(fname2, "%s/player_data/%c/%s.comments", STATS_DIR, newname[0], newname);
1384 rename(fname, fname2);
1385 sprintf(fname, "%s/player_data/%c/%s.logons", STATS_DIR, name[0], name);
1386 sprintf(fname2, "%s/player_data/%c/%s.logons", STATS_DIR, newname[0], newname);
1387 rename(fname, fname2);
1388 sprintf(fname, "%s/player_data/%c/%s.messages", STATS_DIR, name[0], name);
1389 sprintf(fname2, "%s/player_data/%c/%s.messages", STATS_DIR, newname[0], newname);
1390 rename(fname, fname2);
1394 int player_reincarn(char *name, char *newname)
1396 char fname[MAX_FILENAME_SIZE], fname2[MAX_FILENAME_SIZE];
1398 sprintf(fname, "%s/%c/%s", PLAYER_DIR, newname[0], newname);
1399 sprintf(fname2, "%s/%c/.rem.%s", PLAYER_DIR, name[0], name);
1400 rename(fname2, fname);
1401 sprintf(fname, "%s/player_data/%c/%s.games", STATS_DIR, newname[0], newname);
1402 sprintf(fname2, "%s/player_data/%c/.rem.%s.games", STATS_DIR, name[0], name);
1403 rename(fname2, fname);
1404 sprintf(fname, "%s/player_data/%c/%s.comments", STATS_DIR, newname[0], newname);
1405 sprintf(fname2, "%s/player_data/%c/.rem.%s.comments", STATS_DIR, name[0], name);
1406 rename(fname2, fname);
1407 sprintf(fname, "%s/player_data/%c/%s.logons", STATS_DIR, newname[0], newname);
1408 sprintf(fname2, "%s/player_data/%c/.rem.%s.logons", STATS_DIR, name[0], name);
1409 rename(fname2, fname);
1410 sprintf(fname, "%s/player_data/%c/%s.messages", STATS_DIR, newname[0], newname);
1411 sprintf(fname2, "%s/player_data/%c/.rem.%s.messages", STATS_DIR, name[0], name);
1412 rename(fname2, fname);
1416 int player_num_comments(int p)
1418 struct player *pp = &player_globals.parray[p];
1419 char fname[MAX_FILENAME_SIZE];
1421 if (!CheckPFlag(p, PFLAG_REG))
1423 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, pp->login[0],
1424 pp->login, "comments");
1425 return lines_file(fname);
1428 int player_add_comment(int p_by, int p_to, char *comment)
1430 char fname[MAX_FILENAME_SIZE];
1434 if (!CheckPFlag(p_to, PFLAG_REG))
1436 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p_to].login[0],
1437 player_globals.parray[p_to].login, "comments");
1438 fp = fopen_s(fname, "a");
1441 fprintf(fp, "%s at %s: %s\n", player_globals.parray[p_by].name, strltime(&t), comment);
1443 player_globals.parray[p_to].num_comments = player_num_comments(p_to);
1447 int player_show_comments(int p, int p1)
1449 char fname[MAX_FILENAME_SIZE];
1451 if (CheckPFlag(p1, PFLAG_REG)) {
1452 sprintf(fname, "%s/player_data/%c/%s.%s", STATS_DIR, player_globals.parray[p1].login[0],
1453 player_globals.parray[p1].login, "comments");
1454 if (psend_file(p, NULL, fname))
1455 pprintf(p, "There are no comments to show for %s.\n", player_globals.parray[p1].name);
1456 if (player_globals.parray[p1].passwd[0] == '*')
1457 pprintf(p, "%s's account is LOCKED.\n", player_globals.parray[p1].name);
1458 if (in_list(p1, L_BAN, player_globals.parray[p1].name))
1459 pprintf(p, "%s has been BANNED.\n", player_globals.parray[p1].name);
1461 pprintf(p, "Player \"%s\" is unregistered and cannot have comments.\n",
1462 player_globals.parray[p1].name);
1466 /* returns 1 if player is head admin, 0 otherwise */
1467 int player_ishead(int p)
1469 struct player *pp = &player_globals.parray[p];
1470 return (strcasecmp(pp->name, config_get_tmp("HEAD_ADMIN")) == 0);
1473 /* GetRating chooses between blitz, standard and other ratings. */
1474 int GetRating(struct player *p, int gametype)
1476 if (gametype == TYPE_BLITZ) return (p->b_stats.rating);
1477 else if (gametype == TYPE_STAND) return (p->s_stats.rating);
1478 else if (gametype == TYPE_WILD) return (p->w_stats.rating);
1479 else if (gametype == TYPE_LIGHT) return (p->l_stats.rating);
1480 else if (gametype == TYPE_BUGHOUSE) return (p->bug_stats.rating);
1482 } /* end of function GetRating. */
1484 /* GetRD chooses between blitz, standard and other RD's. */
1485 double GetRD(struct player *p, int gametype)
1487 struct statistics *s;
1490 case TYPE_BLITZ: s = &p->b_stats; break;
1491 case TYPE_STAND: s = &p->s_stats; break;
1492 case TYPE_WILD: s = &p->w_stats; break;
1493 case TYPE_LIGHT: s = &p->l_stats; break;
1494 case TYPE_BUGHOUSE: s = &p->bug_stats; break;
1495 default: return 0.0;
1497 return (current_sterr(s->sterr, time(NULL)-(s->ltime)));
1498 } /* end of function GetRD. */