diff options
author | Ratakor <ratakor@disroot.org> | 2023-07-31 15:07:42 +0200 |
---|---|---|
committer | Ratakor <ratakor@disroot.org> | 2023-07-31 15:07:42 +0200 |
commit | 8b703c3f480a1d80d176a77d8ed07595c16f638f (patch) | |
tree | 479f73ae25d45a4c36a15ba70c4c7dcf8a771e3a | |
parent | 8c5a7f8ed77c6f6be2bd240fe83a787edb681a57 (diff) |
Change players to a linked listv0.1.0
Fix write_data() in ocr.c.
Improve cmd_source functions.
Add U32CAST to iterate over the Player struct for uint32_t fields.
Add a mutex for player_head.
Add find_player() to util.c.
Remove MAX_PLAYERS from config.def.h as it's not longer necessary.
Remove libre from the repo.
Remove xfopen() from util.c as it is in libre/x.h.
Change debug flags to main Makefile.
Change qsort to merge sort in cmd_leaderboard.c.
Change multiple boolean value from int to bool type.
Change Player struct to use uint32_t instead of long and to have name
and kingdom in the struct not as pointers.
Change MAX_USERNAME_SIZ and MAX_KINGDOM_SIZ to 32 for no struct padding.
Change try from libre/evil.h to the equivalent from libre/x.h.
Change twice in raids.c for time_run to try more than two times to
recognize the screenshot.
Move discord_send_message() to util.c.
-rw-r--r-- | Makefile | 6 | ||||
-rw-r--r-- | README.md | 11 | ||||
-rw-r--r-- | config.def.h | 3 | ||||
-rw-r--r-- | libre/LICENSE | 13 | ||||
-rw-r--r-- | libre/Makefile | 45 | ||||
-rw-r--r-- | libre/inc/dalloc.h | 59 | ||||
-rw-r--r-- | libre/inc/evil.h | 63 | ||||
-rw-r--r-- | libre/inc/ubik.h | 45 | ||||
-rw-r--r-- | libre/libre.h | 8 | ||||
-rw-r--r-- | libre/src/dalloc.c | 393 | ||||
-rw-r--r-- | libre/src/ubik.c | 228 | ||||
-rw-r--r-- | src/Makefile | 13 | ||||
-rw-r--r-- | src/cmd_correct.c | 56 | ||||
-rw-r--r-- | src/cmd_info.c | 35 | ||||
-rw-r--r-- | src/cmd_lbraid.c | 7 | ||||
-rw-r--r-- | src/cmd_leaderboard.c | 162 | ||||
-rw-r--r-- | src/cmd_source.c | 77 | ||||
-rw-r--r-- | src/init.c | 43 | ||||
-rw-r--r-- | src/main.c | 23 | ||||
-rw-r--r-- | src/nolan.h | 83 | ||||
-rw-r--r-- | src/ocr.c | 42 | ||||
-rw-r--r-- | src/raids.c | 61 | ||||
-rw-r--r-- | src/roles.c | 2 | ||||
-rw-r--r-- | src/stats.c | 253 | ||||
-rw-r--r-- | src/util.c | 40 |
25 files changed, 499 insertions, 1272 deletions
@@ -7,6 +7,7 @@ CC = cc LIBRE_DIR = libre SRC_DIR = src +DFLAGS ?= -O0 -g -DDALLOC CFLAGS ?= -O3 LDFLAGS ?= -s @@ -20,15 +21,12 @@ echo: @echo "LDFLAGS = ${LDFLAGS}" build: - @CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" ${MAKE} -C ${LIBRE_DIR} @CFLAGS="${CFLAGS}" LDFLAGS="${LDFLAGS}" ${MAKE} -C ${SRC_DIR} debug: - @${MAKE} -C ${LIBRE_DIR} - @${MAKE} -C ${SRC_DIR} $@ + @CFLAGS="${DFLAGS}" ${MAKE} -C ${SRC_DIR} clean: - @${MAKE} -C ${LIBRE_DIR} $@ @${MAKE} -C ${SRC_DIR} $@ install: all @@ -5,7 +5,7 @@ A discord bot for Orna. - [concord](https://github.com/Cogmasters/concord) - [tesseract](https://github.com/tesseract-ocr/tesseract) - [gd](https://github.com/libgd/libgd) -- [libre](https://github.com/ratakor/libre) (included) +- [libre](https://github.com/ratakor/libre) ## TODO @@ -13,15 +13,12 @@ A discord bot for Orna. - ascensions (track mats) ### chore -- add mutexes - use config.json instead of config.h -- optimize raids.c and cmd_..raid with slayers -- add documentation - rework roles and make it available to other servers +- add documentation +- rework DIFF in ocr.c and improve accuracy for raids.c - add logging to file -- rework DIFF in ocr.c and raids.c ### misc - use sql for raids and roles -- better wrong image input detection for raids -- async ? +- multi-threading for raids.c diff --git a/config.def.h b/config.def.h index 9320e7b..8f1007b 100644 --- a/config.def.h +++ b/config.def.h @@ -1,11 +1,10 @@ #define DELIM ',' /* Delimiter for the save file */ #define FILENAME "source.csv" /* Filename of the save file */ -#define LB_MAX 10 /* Max players to be shown with ?lb */ +#define LB_MAX 10U /* Max players to be shown with ?lb */ #define APP_ID 0UL /* The bot's application ID */ #define ADMIN 0UL /* Admin user ID for bug reports */ #define RAID_GUILD_ID 0UL /* Server ID for raids */ #define ROLE_GUILD_ID 0UL /* This is only for to Orna FR */ -#define MAX_PLAYERS 50 /* Max players for stats */ #define TOKEN "YOUR-BOT-TOKEN" #define PREFIX "?" diff --git a/libre/LICENSE b/libre/LICENSE deleted file mode 100644 index f1c9616..0000000 --- a/libre/LICENSE +++ /dev/null @@ -1,13 +0,0 @@ -Copyright © 2023, Ratakor <ratakor@disroot.org> - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted, provided that the above -copyright notice and this permission notice appear in all copies. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH -REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND -FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, -INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM -LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR -OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THIS SOFTWARE. diff --git a/libre/Makefile b/libre/Makefile deleted file mode 100644 index 8dc6360..0000000 --- a/libre/Makefile +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright © 2023 Ratakor. See LICENSE file for license details. - -PREFIX ?= /usr/local -LIBDIR = ${DESTDIR}${PREFIX}/lib -INCDIR = ${DESTDIR}${PREFIX}/include/libre -MANDIR = ${DESTDIR}${PREFIX}/share/man/man3 - -LIB = libre.a -LIBS = dalloc ubik - -OBJS = ${LIBS:%=src/%.o} -HEADERS = ${LIBS:%=inc/%.h} inc/evil.h -MANPAGES = ${LIBS:%=man/%.3} man/evil.3 - -CFLAGS += -std=c99 -pedantic -pthread -I./inc -ARFLAGS = -crs - -all: ${LIB} - -.c.o: - ${CC} ${CFLAGS} ${CPPFLAGS} -c $< -o $@ - -${LIB}: ${OBJS} - ${AR} ${ARFLAGS} $@ ${OBJS} - -clean: - rm -f ${LIB} ${OBJS} - -install: all - @mkdir -p ${LIBDIR} - @mkdir -p ${INCDIR} - @mkdir -p ${MANDIR} - cp -f ${LIB} ${LIBDIR} - cp -f ${HEADERS} ${INCDIR} - cp -f ${MANPAGES} ${MANDIR} - @chmod 644 ${LIBDIR}/${LIB} - @chmod 644 ${HEADERS:inc%=${INCDIR}%} - @chmod 644 ${MANPAGES:man%=${MANDIR}%} - -uninstall: - rm -f ${LIBDIR}/${LIB} - rm -rf ${INCDIR} - rm -f ${MANPAGES:man%=${MANDIR}%} - -.PHONY: all clean install uninstall diff --git a/libre/inc/dalloc.h b/libre/inc/dalloc.h deleted file mode 100644 index 21a1eab..0000000 --- a/libre/inc/dalloc.h +++ /dev/null @@ -1,59 +0,0 @@ -/* Copyright © 2023 Ratakor. See LICENSE file for license details. */ - -#ifndef LIBRE_DALLOC_H -#define LIBRE_DALLOC_H - -#include <stdarg.h> -#include <stdlib.h> -#include "ubik.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#if defined(DALLOC) && !defined(DALLOC_INTERNAL) -#define free(p) (_dalloc_free(p, __FILE__, __LINE__)) -#define malloc(siz) (_dalloc_malloc(siz, __FILE__, __LINE__)) -#define calloc(nmemb, siz) (_dalloc_calloc(nmemb, siz, __FILE__, __LINE__)) -#define realloc(p, siz) (_dalloc_realloc(p, siz, __FILE__, __LINE__)) -#define reallocarray(p, n, s) (_dalloc_reallocarray(p, n, s, __FILE__, __LINE__)) -#define memdup(src, siz) (_dalloc_memdup(src, siz, __FILE__, __LINE__)) -#define strdup(s) (_dalloc_strdup(s, __FILE__, __LINE__)) -#define strndup(s, n) (_dalloc_strndup(s, n, __FILE__, __LINE__)) -#define vasprintf(p, fmt, ap) (_dalloc_vasprintf(p, fmt, ap, __FILE__, __LINE__)) -#define asprintf(p, ...) (_dalloc_asprintf(p, __FILE__, __LINE__, __VA_ARGS__)) - -#define dalloc_ignore(p) (_dalloc_ignore(p, __FILE__, __LINE__)) -#define dalloc_comment(p, str) (_dalloc_comment(p, str, __FILE__, __LINE__)) -#endif /* DALLOC && !DALLOC_INTERNAL */ - -#if defined(DALLOC) || defined(DALLOC_INTERNAL) -size_t dalloc_check_overflow(void); -void dalloc_check_free(void); -void dalloc_check_all(void) __attribute__((destructor)); - -void _dalloc_ignore(void *p, char *file, int line); -void _dalloc_comment(void *p, const char *comment, char *file, int line); -void _dalloc_free(void *p, char *file, int line); -void *_dalloc_malloc(size_t siz, char *file, int line); -void *_dalloc_calloc(size_t nmemb, size_t siz, char *file, int line); -void *_dalloc_realloc(void *p, size_t siz, char *file, int line); -void *_dalloc_reallocarray(void *p, size_t n, size_t s, char *file, int line); -void *_dalloc_memdup(const void *src, size_t siz, char *file, int line); -char *_dalloc_strdup(const char *s, char *file, int line); -char *_dalloc_strndup(const char *s, size_t n, char *file, int line); -int _dalloc_vasprintf(char **p, const char *fmt, va_list ap, char *file, int line); -int _dalloc_asprintf(char **p, char *file, int line, const char *fmt, ...); -#else -#define dalloc_ignore(p) -#define dalloc_comment(p, str) -#define dalloc_check_overflow() 0 -#define dalloc_check_free() -#define dalloc_check_all() -#endif /* DALLOC || DALLOC_INTERNAL */ - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* LIBRE_DALLOC_H */ diff --git a/libre/inc/evil.h b/libre/inc/evil.h deleted file mode 100644 index e3e1a19..0000000 --- a/libre/inc/evil.h +++ /dev/null @@ -1,63 +0,0 @@ -/* Copyright © 2023 Ratakor. See LICENSE file for license details. - * Copyright © 2020 Elijah Stone. MIT License for defer macros. - */ - -#ifndef LIBRE_EVIL_H -#define LIBRE_EVIL_H - -#include <stdint.h> -#include "ubik.h" - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#define try(func) _try((void *)(uintptr_t)(func)) -#define itry(func) (int)(uintptr_t)try(func) -#define throw(err) goto jmp##err -#define catch(err) if (0) jmp##err: -#ifndef try_errfunc -#define try_errfunc() die(1, 0) -#endif /* try_errfunc */ - -static inline void * -_try(void *rv) -{ - if (rv == (void *)0 || rv == (void *)-1) { - try_errfunc(); - } - - return rv; -} - -#define defer_init(n) \ - unsigned char _ndeferrals = 0; \ - void *_defer_return_loc = 0, *_deferrals[n] = {0} -#define defer(block) _defer(block, __LINE__) -#define defer_fini() do { _defer_fini(__LINE__) ; } while (0) -#define defer_return(rv) do { _defer_fini(__LINE__) return (rv); } while (0) - -#define _defer_concat(a, b) a ## b -#define _defer(block, n) \ - do { \ - _deferrals[_ndeferrals++] = && _defer_concat(_defer_ini, n); \ - if (0) { \ -_defer_concat(_defer_ini, n): \ - block; \ - if (_ndeferrals) \ - goto *_deferrals[--_ndeferrals]; \ - else \ - goto *_defer_return_loc; \ - } \ - } while (0) -#define _defer_fini(n) \ - if (_ndeferrals) { \ - _defer_return_loc = && _defer_concat(_defer_fini, n); \ - goto *_deferrals[--_ndeferrals]; \ - } _defer_concat(_defer_fini, n): - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* LIBRE_EVIL_H */ diff --git a/libre/inc/ubik.h b/libre/inc/ubik.h deleted file mode 100644 index a47dce5..0000000 --- a/libre/inc/ubik.h +++ /dev/null @@ -1,45 +0,0 @@ -/* Copyright © 2023 Ratakor. See LICENSE file for license details. */ - -#ifndef LIBRE_UBIK_H -#define LIBRE_UBIK_H - -#include <sys/types.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> - -#ifdef __cplusplus -extern "C" { -#endif /* __cplusplus */ - -#define LENGTH(X) (sizeof(X) / sizeof(X[0])) -#define STRLEN(X) (sizeof(X) - 1) -#define MAX(X, Y) ((X) > (Y) ? (X) : (Y)) -#define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) -#define UNUSED(X) ((void)(X)) -#define HOUR(t) ((t / 3600) % 24) -#define MINUTE(t) ((t / 60) % 60) -#define SECONDE(t) (t % 60) -#define btoa(X) (!!(X) ? "true" : "false") - -void warn(const char *fmt, ...); -void die(int status, const char *fmt, ...); -void *memdup(const void *src, size_t siz); -char *nstrchr(const char *s, int c, size_t n); -size_t ufmt(char *dst, size_t dsiz, uintmax_t n); -size_t ifmt(char *dst, size_t dsiz, intmax_t n); -time_t ltime(void); - -size_t strlcpy(char *dst, const char *src, size_t siz); -size_t strlcat(char *dst, const char *src, size_t siz); -void *reallocarray(void *p, size_t nmemb, size_t siz); -char *strdup(const char *s); -char *strndup(const char *s, size_t n); -int vasprintf(char **strp, const char *fmt, va_list ap); -int asprintf(char **strp, const char *fmt, ...); - -#ifdef __cplusplus -} -#endif /* __cplusplus */ - -#endif /* LIBRE_UBIK_H */ diff --git a/libre/libre.h b/libre/libre.h deleted file mode 100644 index 587d255..0000000 --- a/libre/libre.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef LIBRE_H -#define LIBRE_H - -#include "inc/ubik.h" -#include "inc/evil.h" -#include "inc/dalloc.h" - -#endif /* LIBRE_H */ diff --git a/libre/src/dalloc.c b/libre/src/dalloc.c deleted file mode 100644 index 8fe6407..0000000 --- a/libre/src/dalloc.c +++ /dev/null @@ -1,393 +0,0 @@ -/* Copyright © 2023 Ratakor. See LICENSE file for license details. */ - -#include <errno.h> -#include <pthread.h> -#include <stdarg.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> - -#include "ubik.h" -#define DALLOC_INTERNAL -#include "dalloc.h" - -#define EXIT_STATUS 9 -#define OVER_ALLOC 64 -#define MAGIC_NUMBER 0x99 - -#define x1 ((char)(MAGIC_NUMBER)) -#define x2 x1, x1 -#define x4 x2, x2 -#define x8 x4, x4 -#define x16 x8, x8 -#define x32 x16, x16 -#define x64 x32, x32 -#define xCAT(X) x##X -#define MAGIC_INIT(X) xCAT(X) -#define OVERFLOW(p, s) (memcmp(((char *)(p)) + (s), magic_numbers, OVER_ALLOC)) - -typedef struct dalloc_ptr dalloc_ptr; -struct dalloc_ptr { - void *p; - size_t siz; - char *comment; - char *file; - int ignored; - int line; - dalloc_ptr *next; -}; - -static void eprintf(const char *fmt, ...); -static char *xstrdup(const char *s, char *file, int line); -static dalloc_ptr *find_ptr(void *p, char *file, int line); -static void check_overflow(dalloc_ptr *dp, char *file, int line); - -static const char magic_numbers[OVER_ALLOC] = { MAGIC_INIT(OVER_ALLOC) }; -static pthread_mutex_t dalloc_mutex = PTHREAD_MUTEX_INITIALIZER; -static dalloc_ptr *head; - -static void -eprintf(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vfprintf(stderr, fmt, ap); - va_end(ap); -} - -static char * -xstrdup(const char *s, char *file, int line) -{ - char *p; - size_t siz; - - siz = strlen(s) + 1; - if ((p = malloc(siz)) == NULL) { - eprintf("%s:%d: dalloc: %s", file, line, strerror(errno)); - pthread_mutex_unlock(&dalloc_mutex); - exit(EXIT_STATUS); - } - - return memcpy(p, s, siz); -} - -static dalloc_ptr * -find_ptr(void *p, char *file, int line) -{ - dalloc_ptr *dp; - - for (dp = head; dp && dp->p != p; dp = dp->next); - - if (dp == NULL) { - eprintf("%s:%d: dalloc: Unknown pointer %p\n", file, line, p); - pthread_mutex_unlock(&dalloc_mutex); - exit(EXIT_STATUS); - } - - return dp; -} - -static void -check_overflow(dalloc_ptr *dp, char *file, int line) -{ - if (!OVERFLOW(dp->p, dp->siz)) - return; - - eprintf("%s:%d: dalloc: Memory overflow on %p, total: %zu bytes\n" - "The pointer ", file, line, dp->p, dp->siz); - if (dp->comment) - eprintf("'%s' ", dp->comment); - eprintf("was allocated in '%s' on line %d.\n", dp->file, dp->line); - pthread_mutex_unlock(&dalloc_mutex); - exit(EXIT_STATUS); -} - -size_t -dalloc_check_overflow(void) -{ - dalloc_ptr *dp; - size_t sum = 0; - - pthread_mutex_lock(&dalloc_mutex); - eprintf("Memory overflow:"); - for (dp = head; dp; dp = dp->next) { - if (!OVERFLOW(dp->p, dp->siz)) - continue; - - sum++; - eprintf("\n%s:%d: %p, total: %zu bytes", - dp->file, dp->line, dp->p, dp->siz); - if (dp->comment) - eprintf(" /* %s */", dp->comment); - } - pthread_mutex_unlock(&dalloc_mutex); - - if (sum == 0) - eprintf(" 0 overflow :)\n"); - else - eprintf("\nTotal overflow: %zu\n", sum); - - return sum; -} - -void -dalloc_check_free(void) -{ - dalloc_ptr *dp; - size_t n = 0, sum = 0; - - pthread_mutex_lock(&dalloc_mutex); - eprintf("Memory allocated and not freed:"); - for (dp = head; dp; dp = dp->next) { - if (dp->ignored) - continue; - - n++; - sum += dp->siz; - eprintf("\n%s:%d: %p, %zu bytes", - dp->file, dp->line, dp->p, dp->siz); - if (dp->comment) - eprintf(" /* %s */", dp->comment); - } - pthread_mutex_unlock(&dalloc_mutex); - - if (sum == 0) - eprintf(" 0 byte :)\n"); - else - eprintf("\nTotal: %zu bytes, %zu pointers\n", sum, n); -} - -void -dalloc_check_all(void) -{ - dalloc_check_overflow(); - dalloc_check_free(); -} - -void -_dalloc_ignore(void *p, char *file, int line) -{ - dalloc_ptr *dp; - - pthread_mutex_lock(&dalloc_mutex); - dp = find_ptr(p, file, line); - dp->ignored = 1; - pthread_mutex_unlock(&dalloc_mutex); -} - -void -_dalloc_comment(void *p, const char *comment, char *file, int line) -{ - dalloc_ptr *dp; - - if (comment == NULL) - return; - - pthread_mutex_lock(&dalloc_mutex); - dp = find_ptr(p, file, line); - free(dp->comment); - dp->comment = xstrdup(comment, file, line); - pthread_mutex_unlock(&dalloc_mutex); -} - -void -_dalloc_free(void *p, char *file, int line) -{ - dalloc_ptr *dp, *prev; - - if (p == NULL) - return; - - pthread_mutex_lock(&dalloc_mutex); - for (dp = head; dp && dp->p != p; prev = dp, dp = dp->next); - if (dp == NULL) { - eprintf("%s:%d: dalloc: Unknown pointer %p\n", file, line, p); - pthread_mutex_unlock(&dalloc_mutex); - exit(EXIT_STATUS); - } - - check_overflow(dp, file, line); - - if (dp == head) - head = dp->next; - else - prev->next = dp->next; - pthread_mutex_unlock(&dalloc_mutex); - - free(dp->p); - free(dp->file); - free(dp->comment); - free(dp); -} - -void * -_dalloc_malloc(size_t siz, char *file, int line) -{ - dalloc_ptr *dp; - - if (siz == 0) { - eprintf("%s:%d: dalloc: malloc with size == 0\n", file, line); - return NULL; - } - - if (siz + OVER_ALLOC < OVER_ALLOC) { - dp = NULL; - errno = ENOMEM; - } else if ((dp = calloc(1, sizeof(*dp))) != NULL) { - dp->p = malloc(siz + OVER_ALLOC); - } - - if (dp == NULL || dp->p == NULL) { - eprintf("%s:%d: dalloc: %s\n", file, line, strerror(errno)); - exit(EXIT_STATUS); - } - - memset((char *)dp->p + siz, MAGIC_NUMBER, OVER_ALLOC); - dp->siz = siz; - dp->line = line; - - pthread_mutex_lock(&dalloc_mutex); - dp->file = xstrdup(file, file, line); - dp->next = head; - head = dp; - pthread_mutex_unlock(&dalloc_mutex); - - return dp->p; -} - -void * -_dalloc_calloc(size_t nmemb, size_t siz, char *file, int line) -{ - void *p; - - if (siz != 0 && nmemb > (size_t) -1 / siz) { - eprintf("%s:%d: dalloc: calloc: %s\n", - file, line, strerror(ENOMEM)); - exit(EXIT_STATUS); - } - - siz *= nmemb; - p = _dalloc_malloc(siz, file, line); - memset(p, 0, siz); - - return p; -} - -void * -_dalloc_realloc(void *p, size_t siz, char *file, int line) -{ - dalloc_ptr *dp; - - if (p == NULL) - return _dalloc_malloc(siz, file, line); - - if (siz == 0) { - _dalloc_free(p, file, line); - return NULL; - } - - pthread_mutex_lock(&dalloc_mutex); - dp = find_ptr(p, file, line); - check_overflow(dp, file, line); - - if (siz + OVER_ALLOC < OVER_ALLOC) { - dp->p = NULL; - errno = ENOMEM; - } else { - dp->p = realloc(dp->p, siz + OVER_ALLOC); - } - - if (dp->p == NULL) { - eprintf("%s:%d: dalloc: %s\n", file, line, strerror(errno)); - pthread_mutex_unlock(&dalloc_mutex); - exit(EXIT_STATUS); - } - - memset((char *)dp->p + siz, MAGIC_NUMBER, OVER_ALLOC); - dp->siz = siz; - dp->line = line; - free(dp->file); - dp->file = xstrdup(file, file, line); - pthread_mutex_unlock(&dalloc_mutex); - - return dp->p; -} - -void * -_dalloc_reallocarray(void *p, size_t n, size_t s, char *file, int line) -{ - if (s != 0 && n > (size_t) -1 / s) { - eprintf("%s:%d: dalloc: reallocarray: %s\n", - file, line, strerror(ENOMEM)); - exit(EXIT_STATUS); - } - - return _dalloc_realloc(p, n * s, file, line); -} - -void * -_dalloc_memdup(const void *src, size_t siz, char *file, int line) -{ - return memcpy(_dalloc_malloc(siz, file, line), src, siz); -} - -char * -_dalloc_strdup(const char *s, char *file, int line) -{ - return _dalloc_memdup(s, strlen(s) + 1, file, line); -} - -char * -_dalloc_strndup(const char *s, size_t n, char *file, int line) -{ - char *p; - size_t siz; - - siz = MIN(strlen(s), n); - p = _dalloc_malloc(siz + 1, file, line); - memcpy(p, s, siz); - p[siz] = '\0'; - - return p; -} - -int -_dalloc_vasprintf(char **p, const char *fmt, va_list ap, char *file, int line) -{ - va_list ap2; - size_t siz; - int rv; - - va_copy(ap2, ap); - rv = vsnprintf(NULL, 0, fmt, ap2); - va_end(ap2); - if (rv < 0) { - eprintf("%s:%d: dalloc: asprintf: %s\n", - file, line, strerror(errno)); - exit(EXIT_STATUS); - } - siz = (size_t)rv + 1; - *p = _dalloc_malloc(siz, file, line); - rv = vsnprintf(*p, siz, fmt, ap); - if (rv < 0) { - eprintf("%s:%d: dalloc: asprintf: %s\n", - file, line, strerror(errno)); - exit(EXIT_STATUS); - } - - return rv; -} - -int -_dalloc_asprintf(char **p, char *file, int line, const char *fmt, ...) -{ - int rv; - va_list ap; - - va_start(ap, fmt); - rv = _dalloc_vasprintf(p, fmt, ap, file, line); - va_end(ap); - - return rv; -} diff --git a/libre/src/ubik.c b/libre/src/ubik.c deleted file mode 100644 index 04dbc65..0000000 --- a/libre/src/ubik.c +++ /dev/null @@ -1,228 +0,0 @@ -/* Copyright © 2023 Ratakor. See LICENSE file for license details. */ - -#include <errno.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> - -#include "ubik.h" - -extern char *__progname; - -static void -vwarn(const char *fmt, va_list ap) -{ - if (__progname) - fprintf(stderr, "%s: ", __progname); - - if (fmt == NULL) { - perror(NULL); - return; - } - - vfprintf(stderr, fmt, ap); - if (fmt[0] && fmt[strlen(fmt) - 1] == ':') { - fputc(' ', stderr); - perror(NULL); - } else { - fputc('\n', stderr); - } -} - -void -warn(const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vwarn(fmt, ap); - va_end(ap); -} - -void -die(int status, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vwarn(fmt, ap); - va_end(ap); - - exit(status); -} - -void * -memdup(const void *src, size_t siz) -{ - void *p; - - if ((p = malloc(siz)) == NULL) - return NULL; - - return memcpy(p, src, siz); -} - -char * -nstrchr(const char *s, int c, size_t n) -{ - char *p; - - if (n == 0 || s == NULL) - return NULL; - - p = strchr(s, c); - while (--n != 0 && (p = strchr(p + 1, c)) != NULL); - - return p; -} - -size_t -ufmt(char *dst, size_t dsiz, uintmax_t n) -{ - char buf[sizeof(n) * 4], *p; - size_t sizn = 1; - - p = buf + sizeof(buf) - 1; - *p-- = '\0'; - for (;;) { - *p-- = (n % 10) + '0'; - if ((n /= 10) == 0) - break; - if ((sizn % 3) == 0) - *p-- = ','; - sizn++; - } - - return strlcpy(dst, p + 1, dsiz); -} - -size_t -ifmt(char *dst, size_t dsiz, intmax_t n) -{ - if (dsiz == 0) { - if (n < 0) - n = -n; - return ufmt(NULL, 0, (uintmax_t)n) + 1; - } - - if (n < 0) { - *dst = '-'; - n = -n; - } else { - *dst = '+'; - } - - return ufmt(dst + 1, dsiz - 1, (uintmax_t)n) + 1; -} - -time_t -ltime(void) -{ - static time_t tz; - static bool once; - - if (!once) { - const time_t t = time(NULL); - const struct tm *tm = localtime(&t); - time_t tz_hour, tz_min; - - tz_hour = tm->tm_hour - HOUR(t); - tz_min = tm->tm_min - MINUTE(t); - tz = ((tz_hour * 60) + tz_min) * 60; - once = true; - } - - return time(NULL) + tz; -} - -size_t -strlcpy(char *dst, const char *src, size_t siz) -{ - size_t slen, len; - - slen = strlen(src); - if (siz != 0) { - len = MIN(slen, siz - 1); - memcpy(dst, src, len); - dst[len] = '\0'; - } - - return slen; -} - -size_t -strlcat(char *dst, const char *src, size_t siz) -{ - size_t dlen; - - dlen = strlen(dst); - if (dlen >= siz) - return strlen(src) + siz; - - return dlen + strlcpy(dst + dlen, src, siz - dlen); -} - -void * -reallocarray(void *p, size_t nmemb, size_t siz) -{ - if (siz && nmemb > SIZE_MAX / siz) { - errno = ENOMEM; - return NULL; - } - - return realloc(p, nmemb * siz); -} - -char * -strdup(const char *s) -{ - return memdup(s, strlen(s) + 1); -} - -char * -strndup(const char *s, size_t n) -{ - char *p; - size_t siz; - - siz = MIN(strlen(s), n); - if ((p = malloc(siz + 1)) == NULL) - return NULL; - memcpy(p, s, siz); - p[siz] = '\0'; - - return p; -} - -int -vasprintf(char **strp, const char *fmt, va_list ap) -{ - va_list ap2; - int rv; - - va_copy(ap2, ap); - rv = vsnprintf(NULL, 0, fmt, ap2); - va_end(ap2); - - if (rv < 0 || (*strp = malloc((size_t)rv + 1)) == NULL) - return -1; - - return vsnprintf(*strp, (size_t)rv + 1, fmt, ap); -} - -int -asprintf(char **strp, const char *fmt, ...) -{ - va_list ap; - int rv; - - va_start(ap, fmt); - rv = vasprintf(strp, fmt, ap); - va_end(ap); - - return rv; -} diff --git a/src/Makefile b/src/Makefile index bf4bdff..f0a0a42 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2,9 +2,7 @@ BIN = ../nolan CONFIG_H = ../config.h -LIBRE_DIR = ../libre -LIBRE_AR = ${LIBRE_DIR}/libre.a OBJS = cmd_correct.o \ cmd_help.o \ cmd_info.o \ @@ -22,15 +20,15 @@ OBJS = cmd_correct.o \ stats.o \ util.o +RELIBS = -lre DISCORDLIBS = -ldiscord -lcurl -lpthread TESSLIBS = -ltesseract -lleptonica GDLIBS = -lgd -lpng -lz -ljpeg -lfreetype -lm WFLAGS = -pedantic -Werror -Wall -Wextra -Waggregate-return -Wshadow\ -Wmissing-prototypes -Wunused-macros -Wdouble-promotion -DFLAGS = -O0 -g -DDALLOC CFLAGS += -std=c99 -D_DEFAULT_SOURCE ${WFLAGS} -LDFLAGS += ${DISCORDLIBS} ${TESSLIBS} ${GDLIBS} +LDFLAGS += ${RELIBS} ${DISCORDLIBS} ${TESSLIBS} ${GDLIBS} all: ${BIN} @@ -41,15 +39,12 @@ echo: @echo "OBJS = ${OBJS}" ${BIN}: ${CONFIG_H} ${OBJS} - ${CC} ${OBJS} ${LIBRE_AR} ${LDFLAGS} -o $@ + ${CC} ${OBJS} ${LDFLAGS} -o $@ ${CONFIG_H}: @cp ../config.def.h $@ -debug: - @CFLAGS="${DFLAGS}" ${MAKE} - clean: rm -f ${OBJS} ${BIN} -.PHONY: all echo debug clean +.PHONY: all echo clean diff --git a/src/cmd_correct.c b/src/cmd_correct.c index 9b91ec9..61c7cde 100644 --- a/src/cmd_correct.c +++ b/src/cmd_correct.c @@ -166,72 +166,72 @@ write_invalid_value(char *buf, size_t siz, char *fmt, ...) void correct(char *buf, size_t siz, char *category, char *val, u64snowflake userid) { - unsigned int i = 0, j = 0; - long old, new; - size_t s = 0; + Player *player; + uint32_t old, new; + size_t i = 0, s = 0; - while (i < nplayers && players[i].userid != userid) - i++; - - if (i == nplayers) { - strlcpy(buf, "Please register before trying to correct your \ -stats.", siz); + pthread_mutex_lock(&player_mutex); + player = find_player(userid); + if (player == NULL) { + strlcpy(buf, "Please register before trying to correct your " + "stats.", siz); return; } - while (j < LENGTH(fields) - 2 && strcasecmp(fields[j], category) != 0) - j++; + while (i < LENGTH(fields) - 2 && strcasecmp(fields[i], category) != 0) + i++; - if (j == LENGTH(fields) - 2 || j == PLAYTIME) { + if (i == LENGTH(fields) - 2 || i == PLAYTIME) { write_invalid_category(buf, siz); return; } - if (j == NAME) { + if (i == NAME) { if (check_delim(val)) { write_invalid_value(buf, siz, "%c is not a valid char.", DELIM); return; } - if (strlen(val) > MAX_USERNAME_LEN) { + if (strlen(val) >= MAX_USERNAME_SIZ) { write_invalid_value(buf, siz, "Too big username."); return; } snprintf(buf, siz, "<@%lu>\n%s: %s -> %s", userid, - fields[j], players[i].name, val); - strlcpy(players[i].name, val, MAX_USERNAME_LEN); - } else if (j == KINGDOM) { + fields[i], player->name, val); + strlcpy(player->name, val, MAX_USERNAME_SIZ); + } else if (i == KINGDOM) { if (check_delim(val)) { write_invalid_value(buf, siz, "%c is not a valid char.", DELIM); return; } - if (strlen(val) > MAX_KINGDOM_LEN) { + if (strlen(val) >= MAX_KINGDOM_SIZ) { write_invalid_value(buf, siz, "Too big kingdom name."); return; } snprintf(buf, siz, "<@%lu>\n%s: %s -> %s", userid, - fields[j], players[i].kingdom, val); - strlcpy(players[i].kingdom, val, MAX_KINGDOM_LEN); + fields[i], player->kingdom, val); + strlcpy(player->kingdom, val, MAX_KINGDOM_SIZ); } else { - old = ((long *)&players[i])[j]; + old = U32CAST(player)[i]; new = trim_stat(val); - if (new == 0 || new == LONG_MAX) { + if (new == 0 || new == UINT32_MAX) { write_invalid_value(buf, siz, "Too big or zero."); return; } - s += snprintf(buf, siz, "<@%lu>\n%s: ", userid, fields[j]); + s += snprintf(buf, siz, "<@%lu>\n%s: ", userid, fields[i]); s += ufmt(buf + s, siz - s, old); - if (j == DISTANCE) s += strlcpy(buf + s, "m", siz - s); + if (i == DISTANCE) s += strlcpy(buf + s, "m", siz - s); s += strlcpy(buf + s, " -> ", siz - s); s += ufmt(buf + s, siz - s, new); - if (j == DISTANCE) s += strlcpy(buf + s, "m", siz - s); - ((long *)&players[i])[j] = new; + if (i == DISTANCE) s += strlcpy(buf + s, "m", siz - s); + U32CAST(player)[i] = new; } /* TODO: update roles */ - update_file(&players[i]); + update_file(player); + pthread_mutex_unlock(&player_mutex); } char * @@ -270,7 +270,7 @@ on_correct(struct discord *client, const struct discord_message *event) if (strlen(event->content) == 0) { write_invalid_category(buf, sizeof(buf)); } else { - category = try (strdup(event->content)); + category = xstrdup(event->content); if (!(val = get_value(category))) { write_invalid_value(buf, sizeof(buf), "Hint: No value 😜"); diff --git a/src/cmd_info.c b/src/cmd_info.c index 20cf518..cd491e7 100644 --- a/src/cmd_info.c +++ b/src/cmd_info.c @@ -53,9 +53,8 @@ write_invalid(char *buf, size_t siz) void write_info(char *buf, size_t siz, const Player *player) { - unsigned int i; char *playtime; - size_t s = 0; + size_t i, s = 0; /* -2 to not include upadte and userid */ for (i = 0; i < LENGTH(fields) - 2; i++) { @@ -73,13 +72,13 @@ write_info(char *buf, size_t siz, const Player *player) fields[i], player->kingdom); } } else if (i == PLAYTIME) { - playtime = playtime_to_str(((const long *)player)[i]); + playtime = playtime_to_str(U32CAST(player)[i]); s += snprintf(buf + s, siz - s, "%s: %s\n", fields[i], playtime); free(playtime); - } else if (((const long *)player)[i]) { + } else if (U32CAST(player)[i]) { s += snprintf(buf + s, siz - s, "%s: ", fields[i]); - s += ufmt(buf + s, siz - s, ((const long *)player)[i]); + s += ufmt(buf + s, siz - s, U32CAST(player)[i]); if (i == DISTANCE) s += strlcpy(buf + s, "m", siz - s); s += strlcpy(buf + s, "\n", siz - s); } @@ -89,21 +88,21 @@ write_info(char *buf, size_t siz, const Player *player) void info_from_uid(char *buf, size_t siz, u64snowflake userid) { - unsigned int i = 0; + Player *player; - while (i < nplayers && players[i].userid != userid) - i++; - - if (i == nplayers) + pthread_mutex_lock(&player_mutex); + player = find_player(userid); + if (player == NULL) write_invalid(buf, siz); else - write_info(buf, siz, &players[i]); + write_info(buf, siz, player); + pthread_mutex_unlock(&player_mutex); } void info_from_txt(char *buf, size_t siz, char *txt) { - unsigned int i = 0; + Player *player; u64snowflake userid; userid = str_to_uid(txt); @@ -112,12 +111,16 @@ info_from_txt(char *buf, size_t siz, char *txt) return; } - while (i < nplayers && strcasecmp(players[i].name, txt) != 0) - i++; - if (i == nplayers) + pthread_mutex_lock(&player_mutex); + for (player = player_head; player; player = player->next) { + if (strcasecmp(player->name, txt) == 0) + break; + } + if (player == NULL) write_invalid(buf, siz); else - write_info(buf, siz, &players[i]); + write_info(buf, siz, player); + pthread_mutex_unlock(&player_mutex); } void diff --git a/src/cmd_lbraid.c b/src/cmd_lbraid.c index 8d5a757..5d88662 100644 --- a/src/cmd_lbraid.c +++ b/src/cmd_lbraid.c @@ -31,10 +31,7 @@ write_invalid(char *buf, size_t siz) int compare(const void *s1, const void *s2) { - const unsigned int dmg1 = ((const Slayer *)s1)->damage; - const unsigned int dmg2 = ((const Slayer *)s2)->damage; - - return dmg2 - dmg1; + return ((const Slayer *)s2)->damage - ((const Slayer *)s1)->damage; } void @@ -63,7 +60,7 @@ lbraid(char *buf, size_t siz) Slayer *slayers; size_t nslayers, i; - slayers = try (calloc(MAX_SLAYERS, sizeof(*slayers))); + slayers = xcalloc(MAX_SLAYERS, sizeof(*slayers)); nslayers = load_files(slayers); if (nslayers == 0) { write_invalid(buf, siz); diff --git a/src/cmd_leaderboard.c b/src/cmd_leaderboard.c index dfa05fb..68fa422 100644 --- a/src/cmd_leaderboard.c +++ b/src/cmd_leaderboard.c @@ -6,12 +6,15 @@ #include "nolan.h" static void write_invalid(char *buf, size_t siz); -static int compare(const void *p1, const void *p2); -static void write_player(char *buf, size_t siz, unsigned int i, int author); +static void split(Player *head, Player **front, Player **back); +static Player *compare(Player *p1, Player *p2); +static void sort(Player **head); +static void write_player(char *buf, size_t siz, Player *player, unsigned rank, + bool author); static void write_leaderboard(char *buf, size_t siz, u64snowflake userid); static void leaderboard(char *buf, size_t siz, char *txt, u64snowflake userid); -static int category = 0; +static unsigned int category = 0; void create_slash_leaderboard(struct discord *client) @@ -136,41 +139,97 @@ write_invalid(char *buf, size_t siz) } } -int -compare(const void *p1, const void *p2) +void +split(Player *head, Player **front, Player **back) { - const long l1 = ((const long *)(const Player *)p1)[category]; - const long l2 = ((const long *)(const Player *)p2)[category]; + Player *fast, *slow; + + slow = head; + fast = head->next; + + while (fast) { + fast = fast->next; + if (fast) { + slow = slow->next; + fast = fast->next; + } + + } + + *front = head; + *back = slow->next; + slow->next = NULL; +} + +Player * +compare(Player *p1, Player *p2) +{ + Player *res; + uint32_t s1, s2; + + if (p1 == NULL) + return p2; + if (p2 == NULL) + return p1; + + s1 = U32CAST(p1)[category]; + s2 = U32CAST(p2)[category]; if (category == GLOBAL_RANK || category == COMPETITIVE_RANK) { - if (l1 == 0) - return 1; - if (l2 == 0) - return -1; - return l1 - l2; + if (s2 == 0 || (s1 != 0 && s1 < s2)) { + res = p1; + res->next = compare(p1->next, p2); + } else { + res = p2; + res->next = compare(p1, p2->next); + } + } else { + if (s1 > s2) { + res = p1; + res->next = compare(p1->next, p2); + } else { + res = p2; + res->next = compare(p1, p2->next); + } + } - return l2 - l1; + return res; +} + +void +sort(Player **head) +{ + Player *front, *back; + + if ((head == NULL) || (*head == NULL) || ((*head)->next == NULL)) + return; + + split(*head, &front, &back); + sort(&front); + sort(&back); + + *head = compare(front, back); } void -write_player(char *buf, size_t siz, unsigned int i, int author) +write_player(char *buf, size_t siz, Player *player, unsigned rank, bool author) { - size_t ssiz = 32; - char *plt, stat[ssiz]; + char *plt, stat[32]; if (author) - snprintf(buf, siz, "%d. **%s**: ", i + 1, players[i].name); + snprintf(buf, siz, "%u. **%s**: ", rank, player->name); else - snprintf(buf, siz, "%d. %s: ", i + 1, players[i].name); + snprintf(buf, siz, "%u. %s: ", rank, player->name); if (category == PLAYTIME) { - plt = playtime_to_str(((long *)&players[i])[category]); + plt = playtime_to_str(U32CAST(player)[category]); strlcat(buf, plt, siz); free(plt); } else { - ufmt(stat, ssiz, ((long *)&players[i])[category]); + ufmt(stat, sizeof(stat), U32CAST(player)[category]); strlcat(buf, stat, siz); - if (i == DISTANCE) strlcat(buf, "m", siz); + if (category == DISTANCE) + strlcat(buf, "m", siz); } strlcat(buf, "\n", siz); } @@ -178,49 +237,56 @@ write_player(char *buf, size_t siz, unsigned int i, int author) void write_leaderboard(char *buf, size_t siz, u64snowflake userid) { - int in_lb = 0; - unsigned int i, lb_max = MIN(nplayers, LB_MAX); - char player[LINE_SIZE]; - /* siz = (lb_max + 2) * 64; */ + Player *player; + char player_buf[256]; + unsigned int rank = 1; + bool in_lb = false; strlcpy(buf, fields[category], siz); strlcat(buf, ":\n", siz); - for (i = 0; i < lb_max; i++) { - if (userid == players[i].userid) { - in_lb = 1; - write_player(player, sizeof(player), i, 1); + for (player = player_head; player; player = player->next, rank++) { + if (rank > LB_MAX) + break; + + if (player->userid == userid) { + in_lb = true; + write_player(player_buf, sizeof(player_buf), player, + rank, true); } else { - write_player(player, sizeof(player), i, 0); + write_player(player_buf, sizeof(player_buf), player, + rank, false); } - if (strlcat(buf, player, siz) >= siz) { - log_warn("%s: string truncation\n\ -\033[33mhint:\033[39m this is probably because LB_MAX is too big", __func__); + if (strlcat(buf, player_buf, siz) >= siz) { + log_warn("%s: string truncation\n" + "\033[33mhint:\033[39m this is probably " + "because LB_MAX is too big", __func__); return; } } - if (!in_lb) { - while (i < nplayers && players[i].userid != userid) - i++; - if (i == nplayers) /* not a player */ - return; - strlcat(buf, "...\n", siz); - write_player(player, sizeof(player), i, 1); - if (strlcat(buf, player, siz) >= siz) { - log_warn("%s: string truncation\n\ -\033[33mhint:\033[39m this is probably because LB_MAX is too big", __func__); - } + if (in_lb) + return; + + for (; player && player->userid != userid; player = player->next, rank++); + if (player == NULL) /* not a player */ + return; + + strlcat(buf, "...\n", siz); + write_player(player_buf, sizeof(player_buf), player, rank, true); + if (strlcat(buf, player_buf, siz) >= siz) { + log_warn("%s: string truncation\n\033[33mhint:\033[39m this " + "is probably because LB_MAX is too big", __func__); } } void -leaderboard(char *buf, size_t siz, char *categ, u64snowflake userid) +leaderboard(char *buf, size_t siz, char *txt, u64snowflake userid) { unsigned int i = 2; /* ignore name and kingdom */ /* -2 to not include userid and update */ while (i < LENGTH(fields) - 2 && - strcasecmp(fields[i], categ) != 0) + strcasecmp(fields[i], txt) != 0) i++; if (i == LENGTH(fields) - 2 || i == REGIONAL_RANK) { @@ -228,9 +294,11 @@ leaderboard(char *buf, size_t siz, char *categ, u64snowflake userid) return; } + pthread_mutex_lock(&player_mutex); category = i; - qsort(players, nplayers, sizeof(players[0]), compare); + sort(&player_head); write_leaderboard(buf, siz, userid); + pthread_mutex_unlock(&player_mutex); } void diff --git a/src/cmd_source.c b/src/cmd_source.c index d1954ac..195b481 100644 --- a/src/cmd_source.c +++ b/src/cmd_source.c @@ -5,8 +5,8 @@ #include "nolan.h" -static char *load_source(size_t *fszp); -static char *load_sorted_source(size_t *fszp, char *kingdom); +static char *load_source(void); +static char *load_sorted_source(char *kingdom); void create_slash_source(struct discord *client) @@ -30,71 +30,74 @@ create_slash_source(struct discord *client) discord_create_global_application_command(client, APP_ID, &cmd, NULL); } + char * -load_source(size_t *fszp) +load_source(void) { FILE *fp; - char *res, line[LINE_SIZE], *end; - size_t mfsz = LINE_SIZE + nplayers * LINE_SIZE; + char *res = NULL, line[LINE_SIZE], *end; + size_t llen, len = 0; fp = xfopen(STATS_FILE, "r"); - res = try (malloc(mfsz)); - *res = '\0'; - while (fgets(line, LINE_SIZE, fp) != NULL) { + while (fgets(line, sizeof(line), fp)) { /* skip everything after codex */ if ((end = nstrchr(line, DELIM, CODEX + 1))) { *end = '\n'; *(end + 1) = '\0'; } - strlcat(res, line, mfsz); + llen = strlen(line); + res = xrealloc(res, len + llen + 1); + memcpy(res + len, line, llen); + len += llen; } - fclose(fp); - *fszp = strlen(res); + return res; } char * -load_sorted_source(size_t *fszp, char *kingdom) +load_sorted_source(char *kingdom) { FILE *fp; char *res, line[LINE_SIZE], *kd, *endkd, *end; - size_t mfsz = LINE_SIZE + nplayers * LINE_SIZE; + size_t llen, len; fp = xfopen(STATS_FILE, "r"); - res = try (malloc(mfsz)); - fgets(line, LINE_SIZE, fp); /* fields name */ + if (fgets(line, LINE_SIZE, fp) == NULL) /* fields name */ + die(1, "Field name line in %s is wrong", STATS_FILE); /* skip everything after codex */ if ((end = nstrchr(line, DELIM, CODEX + 1))) { *end = '\n'; *(end + 1) = '\0'; } - strlcpy(res, line, mfsz); - while (fgets(line, LINE_SIZE, fp) != NULL) { + res = xstrdup(line); + len = strlen(res); + while (fgets(line, LINE_SIZE, fp)) { kd = strchr(line, DELIM) + 1; - if ((endkd = nstrchr(line, DELIM, 2))) + if ((endkd = nstrchr(line, DELIM, KINGDOM + 1))) * endkd = '\0'; - if (strcmp(kd, kingdom) == 0) { - if (endkd) - *endkd = DELIM; - /* skip everything after codex */ - if ((end = nstrchr(line, DELIM, CODEX + 1))) { - *end = '\n'; - *(end + 1) = '\0'; - } - strlcat(res, line, mfsz); + if (strcmp(kd, kingdom) != 0) + continue; + if (endkd) + *endkd = DELIM; + /* skip everything after codex */ + if ((end = nstrchr(line, DELIM, CODEX + 1))) { + *end = '\n'; + *(end + 1) = '\0'; } + llen = strlen(line); + res = xrealloc(res, len + llen + 1); + memcpy(res + len, line, llen); + len += llen; } - fclose(fp); - *fszp = strlen(res); + return res; } void on_source(struct discord *client, const struct discord_message *event) { - size_t fsiz = 0; char *fbuf = NULL; if (event->author->bot) @@ -107,14 +110,14 @@ on_source(struct discord *client, const struct discord_message *event) log_info("%s", __func__); if (strlen(event->content) == 0) - fbuf = load_source(&fsiz); + fbuf = load_source(); else - fbuf = load_sorted_source(&fsiz, event->content); + fbuf = load_sorted_source(event->content); struct discord_attachment attachment = { .filename = FILENAME, .content = fbuf, - .size = fsiz + .size = strlen(fbuf) }; struct discord_attachments attachments = { .size = 1, @@ -131,20 +134,18 @@ void on_source_interaction(struct discord *client, const struct discord_interaction *event) { - size_t fsiz = 0; char *fbuf = NULL; log_info("%s", __func__); if (!event->data->options) - fbuf = load_source(&fsiz); + fbuf = load_source(); else - fbuf = load_sorted_source(&fsiz, - event->data->options->array[0].value); + fbuf = load_sorted_source(event->data->options->array[0].value); struct discord_attachment attachment = { .filename = FILENAME, .content = fbuf, - .size = fsiz + .size = strlen(fbuf) }; struct discord_attachments attachments = { .size = 1, @@ -2,7 +2,6 @@ #include <sys/stat.h> -#include <err.h> #include <stdlib.h> #include <stdio.h> #include <string.h> @@ -24,7 +23,7 @@ create_folders(void) continue; if (mkdir(folders[i], 0755) == -1) - err(1, "mkdir: %s", folders[i]); + die(1, "mkdir: %s:", folders[i]); else log_info("Created %s", folders[i]); } @@ -76,12 +75,15 @@ void init_players(void) { FILE *fp; + Player *player; char line[LINE_SIZE], *p, *delim; - unsigned int i; + size_t i, line_counter = 1; fp = xfopen(STATS_FILE, "r"); fgets(line, LINE_SIZE, fp); /* discard description line */ while ((p = fgets(line, LINE_SIZE, fp)) != NULL) { + line_counter++; + player = xcalloc(1, sizeof(*player)); i = 0; delim = p; /* -1 because the last field in the file finish with a '\n' */ @@ -90,27 +92,32 @@ init_players(void) continue; *delim = '\0'; - if (i == NAME) { - players[nplayers].name = try (calloc(1, MAX_USERNAME_LEN)); - strlcpy(players[nplayers].name, p, MAX_USERNAME_LEN); - } else if (i == KINGDOM) { - players[nplayers].kingdom = try (calloc(1, MAX_KINGDOM_LEN)); - strlcpy(players[nplayers].kingdom, p, MAX_KINGDOM_LEN); - } else { - ((long *)&players[nplayers])[i] = atol(p); + switch (i) { + case NAME: + strlcpy(player->name, p, MAX_USERNAME_SIZ); + break; + case KINGDOM: + strlcpy(player->kingdom, p, MAX_KINGDOM_SIZ); + break; + case UPDATE: + player->update = strtol(p, NULL, 10); + break; + default: + U32CAST(player)[i] = strtoul(p, NULL, 10); + break; } p = delim + 1; i++; } if (i != LENGTH(fields) - 1) { - errx(1, "Player in %s on line %lu is missing a field", - STATS_FILE, nplayers + 1); + die(1, "Player in %s on line %zu is missing a field", + STATS_FILE, line_counter); } - players[nplayers].userid = strtoul(p, NULL, 10); - - if (++nplayers > MAX_PLAYERS) - errx(1, "There is too much players to load (max:%d)", - MAX_PLAYERS); + player->userid = strtoull(p, NULL, 10); + pthread_mutex_lock(&player_mutex); + player->next = player_head; + player_head = player; + pthread_mutex_unlock(&player_mutex); } fclose(fp); } @@ -9,8 +9,8 @@ #include "nolan.h" static struct discord *client; -Player players[MAX_PLAYERS]; -size_t nplayers; +Player *player_head; +pthread_mutex_t player_mutex = PTHREAD_MUTEX_INITIALIZER; const char *fields[] = { "Name", "Kingdom", @@ -53,17 +53,20 @@ log_callback(log_Event *ev) fflush(ev->udata); } -void +static void cleanup(void) { - size_t i; + Player *player, *next; discord_cleanup(client); ccord_global_cleanup(); - for (i = 0; i < nplayers; i++) { - free(players[i].name); - free(players[i].kingdom); + pthread_mutex_lock(&player_mutex); + for (player = player_head; player; player = next) { + next = player->next; + free(player); } + player_head = NULL; + pthread_mutex_unlock(&player_mutex); } static void @@ -79,9 +82,6 @@ main(void) char *src[] = { "src", "source" }; char *lb[] = { "lb", "leaderboard" }; - setlocale(LC_ALL, ""); - signal(SIGINT, &sighandler); - create_folders(); create_stats_file(); init_players(); @@ -108,6 +108,9 @@ main(void) discord_set_on_command(client, "correct", on_correct); discord_set_on_command(client, "time", on_time); + setlocale(LC_ALL, ""); + signal(SIGINT, &sighandler); + discord_run(client); cleanup(); diff --git a/src/nolan.h b/src/nolan.h index dab67cb..f8e18e2 100644 --- a/src/nolan.h +++ b/src/nolan.h @@ -3,22 +3,21 @@ #ifndef NOLAN_H #define NOLAN_H +#include <pthread.h> #include <concord/discord.h> #include <concord/log.h> +#include <libre/ubik.h> +#include <libre/x.h> +#include <libre/dalloc.h> -/* main.c */ -void cleanup(void); - -#define try_errfunc() do { cleanup(); die(1, 0); } while (0) -#include "../libre/libre.h" #include "../config.h" /* normally 50 but let's do * 3 to prevent from buffer overflow :) */ #define MAX_SLAYERS 50 * 3 #define LINE_SIZE 300 + 1 #define MAX_MESSAGE_LEN 2000 + 1 -#define MAX_USERNAME_LEN 32 + 1 -#define MAX_KINGDOM_LEN 32 + 1 +#define MAX_USERNAME_SIZ 32 +#define MAX_KINGDOM_SIZ 32 #ifdef DEVEL #define SAVE_FOLDER "./" @@ -29,6 +28,8 @@ void cleanup(void); #define IMAGES_FOLDER SAVE_FOLDER "images/" #define RAIDS_FOLDER SAVE_FOLDER "raids/" #define STATS_FILE SAVE_FOLDER FILENAME +#define U32CAST(player)\ + ((uint32_t *)((char *)(player) + MAX_USERNAME_SIZ + MAX_KINGDOM_SIZ) - 2) enum { NAME, @@ -57,49 +58,51 @@ enum { USERID, }; -typedef struct { - /* put char and kd at the end */ - char *name; /* TODO: change this to char name[MAX_USERNAME_LEN] */ - char *kingdom; /* TODO: change this to char kingdom[MAX_KINGDOM_LEN] */ - long level; /* TODO: change to uint64_t */ - long ascension; - long global; - long regional; - long competitive; - long playtime; - long monsters; - long bosses; - long players; - long quests; - long explored; - long taken; - long dungeons; - long coliseum; - long items; - long fish; - long distance; - long reputation; - long endless; - long codex; +typedef struct Player Player; +struct Player { + char name[MAX_USERNAME_SIZ]; + char kingdom[MAX_KINGDOM_SIZ]; + uint32_t level; + uint32_t ascension; + uint32_t global; + uint32_t regional; + uint32_t competitive; + uint32_t playtime; + uint32_t monsters; + uint32_t bosses; + uint32_t players; + uint32_t quests; + uint32_t explored; + uint32_t taken; + uint32_t dungeons; + uint32_t coliseum; + uint32_t items; + uint32_t fish; + uint32_t distance; + uint32_t reputation; + uint32_t endless; + uint32_t codex; time_t update; u64snowflake userid; -} Player; + Player *next; +}; /* this is for raids */ typedef struct { char *name; uint32_t damage; - int found_in_file; + bool found_in_file; } Slayer; -/* TODO: change this to a linked list */ -extern Player players[MAX_PLAYERS]; -extern size_t nplayers; +extern Player *player_head; +extern pthread_mutex_t player_mutex; extern const char *fields[24]; /* util.c */ -FILE *xfopen(const char *filename, const char *mode); int file_exists(const char *filename); +Player *find_player(u64snowflake userid); +void discord_send_message(struct discord *client, u64snowflake channel_id, + const char *fmt, ...); /* init.c */ void create_folders(void); @@ -122,8 +125,8 @@ char *ocr(const char *fname, const char *lang); /* stats.c */ void create_slash_stats(struct discord *client); bool check_delim(const char *val); -long trim_stat(const char *str); -char *playtime_to_str(long playtime); +uint32_t trim_stat(const char *str); +char *playtime_to_str(uint32_t playtime); void update_file(Player *player); void on_stats(struct discord *client, const struct discord_message *event); void on_stats_interaction(struct discord *client, @@ -185,6 +188,6 @@ void on_correct_interaction(struct discord *client, void create_slash_time(struct discord *client); void on_time(struct discord *client, const struct discord_message *event); void on_time_interaction(struct discord *client, - const struct discord_interaction *event); + const struct discord_interaction *event); #endif /* NOLAN_H */ @@ -5,7 +5,7 @@ #include <leptonica/allheaders.h> #include <tesseract/capi.h> -#include <err.h> +#include <errno.h> #include <string.h> #include "nolan.h" @@ -14,39 +14,57 @@ #define DIFF 110 #define WHITE 12000000 +struct Slice { + char *data; + size_t siz; +}; + static size_t write_data(void *ptr, size_t size, size_t nmemb, void *stream); static int write_rect(gdRect *rect, gdImagePtr im); size_t -write_data(void *ptr, size_t size, size_t nmemb, void *stream) +write_data(void *ptr, size_t siz, size_t nmemb, void *stream) { - if (size * nmemb > MAX_MESSAGE_LEN) - errx(1, "%s:%d %s: fix your code", __FILE__, __LINE__, __func__); - return strlcpy(stream, ptr, MAX_MESSAGE_LEN); + struct Slice *buf; + + if (siz && nmemb > (size_t) -1 / siz) + die(1, "realloc: %s", strerror(ENOMEM)); + + siz *= nmemb; + buf = (struct Slice *)stream; + buf->data = xrealloc(buf->data, buf->siz + siz + 1); + memcpy(buf->data + buf->siz, ptr, siz); + buf->siz += siz + 1; + buf->data[buf->siz - 1] = '\0'; + + return buf->siz; } char * curl(char *url) { - CURL *handle = curl_easy_init(); - char *buf = NULL; + CURL *handle; + struct Slice buf = { .data = NULL, .siz = 0 }; + handle = curl_easy_init(); curl_easy_setopt(handle, CURLOPT_URL, url); curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(handle, CURLOPT_WRITEDATA, &buf); curl_easy_perform(handle); curl_easy_cleanup(handle); - return buf; + return buf.data; } unsigned int curl_file(char *url, char *fname) { - CURL *handle = curl_easy_init(); + CURL *handle; CURLcode ret; - FILE *fp = xfopen(fname, "wb"); + FILE *fp; + handle = curl_easy_init(); + fp = xfopen(fname, "wb"); curl_easy_setopt(handle, CURLOPT_URL, url); /* curl uses fwrite by default */ curl_easy_setopt(handle, CURLOPT_WRITEDATA, fp); @@ -139,10 +157,10 @@ ocr(const char *fname, const char *lang) TessBaseAPISetImage2(handle, img); if (TessBaseAPIRecognize(handle, NULL) != 0) - errx(1, "Failed tesseract recognition"); + die(1, "Failed tesseract recognition"); txt_ocr = TessBaseAPIGetUTF8Text(handle); - txt_out = try (strdup(txt_ocr)); + txt_out = xstrdup(txt_ocr); TessDeleteText(txt_ocr); TessBaseAPIEnd(handle); diff --git a/src/raids.c b/src/raids.c index 554281a..18cf9c4 100644 --- a/src/raids.c +++ b/src/raids.c @@ -18,9 +18,6 @@ enum { }; -static void discord_send_message(struct discord *client, - const u64snowflake channel_id, - const char *fmt, ...); static char *skip_to_slayers(char *txt); static char *trim_name(char *name); static uint32_t trim_dmg(char *str); @@ -50,34 +47,12 @@ static const char *garbageslayer[] = { "討 伐 者" }; -/* this should be in util.c but it's only used here*/ -void -discord_send_message(struct discord *client, const u64snowflake channel_id, - const char *fmt, ...) -{ - char buf[MAX_MESSAGE_LEN]; - size_t rsiz; - va_list ap; - - va_start(ap, fmt); - rsiz = vsnprintf(buf, sizeof(buf), fmt, ap); - va_end(ap); - - if (rsiz >= sizeof(buf)) - log_warn("%s: string truncation", __func__); - - struct discord_create_message msg = { - .content = buf - }; - discord_create_message(client, channel_id, &msg, NULL); -} - char * skip_to_slayers(char *txt) { char *line = txt, *endline; unsigned int i; - int found = 0; + bool found = false; while (line && !found) { endline = strchr(line, '\n'); @@ -85,7 +60,7 @@ skip_to_slayers(char *txt) *endline = '\0'; for (i = 0; i < LENGTH(delims); i++) { if (strcmp(line, delims[i]) == 0) { - found = 1; + found = true; break; } } @@ -158,7 +133,7 @@ get_slayers(Slayer slayers[], char *txt) char *line = txt, *endline; size_t nslayers = 0; unsigned int i; - int found = 0; + bool found = false; if (txt == NULL) return 0; @@ -179,7 +154,7 @@ get_slayers(Slayer slayers[], char *txt) if (!found) { /* skip Slayer */ for (i = 0; i < LENGTH(garbageslayer); i++) { if (strcmp(line, garbageslayer[i]) == 0) { - found = 1; + found = true; break; } } @@ -189,7 +164,7 @@ get_slayers(Slayer slayers[], char *txt) } } - slayers[nslayers].name = try (strdup(trim_name(line))); + slayers[nslayers].name = xstrdup(trim_name(line)); dalloc_comment(slayers[nslayers].name, "slayer name"); line = endline + 1; @@ -215,7 +190,7 @@ get_slayers(Slayer slayers[], char *txt) break; } line = endline + 1; - slayers[nslayers].found_in_file = 0; + slayers[nslayers].found_in_file = false; nslayers++; } @@ -263,7 +238,7 @@ save_to_file(Slayer slayers[], size_t nslayers) continue; if (strncasecmp(slayers[i].name, line, strlen(slayers[i].name)) == 0) { - slayers[i].found_in_file = 1; + slayers[i].found_in_file = true; break; } } @@ -296,8 +271,8 @@ raids(struct discord_attachment *attachment, const char *lang, Slayer slayers[]) { char *txt = NULL, fname[PATH_MAX]; size_t nslayers; - CURLcode ret; - int is_png; + CURLcode code; + bool is_png; is_png = (strcmp(attachment->content_type, "image/png") == 0); if (is_png) @@ -307,8 +282,8 @@ raids(struct discord_attachment *attachment, const char *lang, Slayer slayers[]) snprintf(fname, sizeof(fname), "%s/%s.jpg", IMAGES_FOLDER, attachment->filename); - if ((ret = curl_file(attachment->url, fname)) != 0) { - log_error("curl failed CURLcode: %u", ret); + if ((code = curl_file(attachment->url, fname)) != 0) { + log_error("curl failed CURLcode: %u", code); return DOWNLOAD_FAILED; } @@ -346,7 +321,7 @@ parse_file(char *fname, Slayer slayers[], size_t *nslayers) if (i < *nslayers) { slayers[i].damage += dmg; } else { - slayers[i].name = try (strdup(line)); + slayers[i].name = xstrdup(line); dalloc_comment(slayers[i].name, "parse_file slayers name"); slayers[i].damage = dmg; @@ -407,7 +382,7 @@ on_raids(struct discord *client, const struct discord_message *event) Slayer *slayers; char *lang; size_t i; - int twice = 0; + unsigned int time_run = 0; for (i = 0; i < LENGTH(cn_slayer_ids); i++) { if (event->author->id == cn_slayer_ids[i]) { @@ -425,7 +400,7 @@ on_raids(struct discord *client, const struct discord_message *event) lang_found: - slayers = try (calloc(MAX_SLAYERS, sizeof(*slayers))); + slayers = xcalloc(MAX_SLAYERS, sizeof(*slayers)); for (i = 0; i < (size_t)event->attachments->size; i++) { run_again: switch (raids(event->attachments->array + i, lang, slayers)) { @@ -449,11 +424,11 @@ run_again: break; case PARSING_FAILED: /* TODO: this is ugly */ - if (!twice) { - twice = 1; + if (time_run < 3) { + time_run++; goto run_again; } - log_error("parsing failed twice with lang:%s from %s", + log_error("parsing failed with lang:%s from %s", lang, event->author->username); /* TODO: just me being lazy */ if (strcmp(lang, "jpn") == 0) { @@ -466,7 +441,7 @@ run_again: } break; } - twice = 0; + time_run = 0; memset(slayers, 0, MAX_SLAYERS * sizeof(*slayers)); } diff --git a/src/roles.c b/src/roles.c index b63265a..ff4be18 100644 --- a/src/roles.c +++ b/src/roles.c @@ -59,7 +59,7 @@ level(struct discord *client, u64snowflake userid, Player *player) void playtime(struct discord *client, u64snowflake userid, Player *player) { - long playtime = player->playtime / 24; + uint32_t playtime = player->playtime / 24; if (playtime >= 150) { discord_add_guild_member_role(client, ROLE_GUILD_ID, userid, diff --git a/src/stats.c b/src/stats.c index be24bcc..cf73f71 100644 --- a/src/stats.c +++ b/src/stats.c @@ -1,6 +1,5 @@ /* Copywrong © 2023 Ratakor. See LICENSE file for license details. */ -#include <err.h> #include <stdbool.h> #include <stdlib.h> #include <string.h> @@ -12,20 +11,20 @@ struct Field { const char *key; - const int val; + const unsigned int val; const unsigned int keylen; }; enum { ENGLISH, FRENCH }; -static long playtime_to_long(char *playtime, int lang); +static uint32_t playtime_to_u32(char *playtime, int lang); static void parse_line(Player *player, char *line); static void for_line(Player *player, char *txt); static char *get_quote(void); static size_t write_quote(char *buf, size_t siz); -static void create_player(Player *player, unsigned int i); -static void update_player(char *buf, size_t siz, Player *player, unsigned int i); -static unsigned int update_players(char *buf, size_t siz, Player *player); +static void update_player(char *buf, size_t siz, Player *player, + Player *new_player); +static Player *update_players(char *buf, size_t siz, Player *new_player); static void stats(char *buf, size_t siz, char *url, char *username, u64snowflake userid, u64snowflake guild_id, struct discord *client); @@ -115,61 +114,70 @@ check_delim(const char *val) return false; } -long -playtime_to_long(char *playtime, int lang) +uint32_t +playtime_to_u32(char *playtime, int lang) { - char *p, dayname[32], *pdn = dayname; - long days, hours; + const char *dayname = NULL, *pdn; + char *p; + uint32_t days, hours; if (lang == ENGLISH) - strlcpy(dayname, "days, ", sizeof(dayname)); + dayname = "days, "; else if (lang == FRENCH) - strlcpy(dayname, "jours, ", sizeof(dayname)); + dayname = "jours, "; else - errx(1, "%s:%d %s: New language not correctly added", - __FILE__, __LINE__, __func__); + die(1, "%s:%d %s: New language not correctly added", + __FILE__, __LINE__, __func__); - days = strtol(playtime, NULL, 10); + days = strtoul(playtime, NULL, 10); if ((p = strchr(playtime, dayname[0])) == 0) return days; /* less than a day of playtime */ + pdn = dayname; while (*pdn && (*p++ == *pdn++)); - hours = strtol(p, NULL, 10); + hours = strtoul(p, NULL, 10); return days * 24 + hours; } char * -playtime_to_str(long playtime) +playtime_to_str(uint32_t playtime) { - long days = playtime / 24; - long hours = playtime % 24; - size_t siz = 36; + uint32_t days, hours; + const size_t siz = 36; char *buf; - buf = try (malloc(siz)); + days = playtime / 24; + hours = playtime % 24; + buf = xmalloc(siz); dalloc_comment(buf, "playtime_to_str buf"); switch (hours) { case 0: if (days <= 1) - snprintf(buf, siz, "%ld day", days); + snprintf(buf, siz, "%"PRIu32" day", days); else - snprintf(buf, siz, "%ld days", days); + snprintf(buf, siz, "%"PRIu32" days", days); break; case 1: - if (days == 0) - snprintf(buf, siz, "%ld hour", hours); - else if (days == 1) - snprintf(buf, siz, "%ld day, %ld hour", days, hours); - else - snprintf(buf, siz, "%ld days, %ld hour", days, hours); + if (days == 0) { + snprintf(buf, siz, "%"PRIu32" hour", hours); + } else if (days == 1) { + snprintf(buf, siz, "%"PRIu32" day, %"PRIu32" hour", + days, hours); + } else { + snprintf(buf, siz, "%"PRIu32" days, %"PRIu32" hour", + days, hours); + } break; default: - if (days == 0) - snprintf(buf, siz, "%ld hours", hours); - else if (days == 1) - snprintf(buf, siz, "%ld day, %ld hours", days, hours); - else - snprintf(buf, siz, "%ld days, %ld hours", days, hours); + if (days == 0) { + snprintf(buf, siz, "%"PRIu32" hours", hours); + } else if (days == 1) { + snprintf(buf, siz, "%"PRIu32" day, %"PRIu32" hours", + days, hours); + } else { + snprintf(buf, siz, "%"PRIu32" days, %"PRIu32" hours", + days, hours); + } break; } @@ -177,10 +185,10 @@ playtime_to_str(long playtime) } /* trim everything that is not a number and replace | with 1 */ -long +uint32_t trim_stat(const char *str) { - long stat = 0; + uint32_t stat = 0; do { if (*str >= '0' && *str <= '9') @@ -198,7 +206,7 @@ parse_line(Player *player, char *line) const struct Field *field; unsigned int lang, i; size_t langlen; - long stat; + uint32_t stat; for (lang = 0; lang < LENGTH(languages); lang++) { if (lang == ENGLISH) @@ -206,8 +214,8 @@ parse_line(Player *player, char *line) else if (lang == FRENCH) langlen = LENGTH(french); else - errx(1, "%s:%d %s: New language not correctly added", - __FILE__, __LINE__, __func__); + die(1, "%s:%d %s: New language not correctly added", + __FILE__, __LINE__, __func__); for (i = 0; i < langlen; i++) { field = &languages[lang][i]; @@ -224,10 +232,11 @@ parse_line(Player *player, char *line) switch (field->val) { case KINGDOM: if (!check_delim(line + field->keylen + 1)) - player->kingdom = line + field->keylen + 1; + strlcpy(player->kingdom, line + field->keylen + 1, + MAX_KINGDOM_SIZ); break; case PLAYTIME: - player->playtime = playtime_to_long(line + field->keylen, lang); + player->playtime = playtime_to_u32(line + field->keylen, lang); break; case LEVEL: stat = trim_stat(line); @@ -237,7 +246,7 @@ parse_line(Player *player, char *line) default: stat = trim_stat(line); if (stat) - ((long *)player)[field->val] = stat; + U32CAST(player)[field->val] = stat; break; } } @@ -295,7 +304,7 @@ get_quote(void) /* change '*' to "\*", must need a large enough buffer */ p = quote; while ((p = strchr(p, '*')) != NULL) { - tmp = try (strdup(p)); + tmp = xstrdup(p); op = p; otmp = tmp; *p++ = '\\'; @@ -321,48 +330,31 @@ write_quote(char *buf, size_t siz) } void -create_player(Player *player, unsigned int i) -{ - unsigned int j; - - players[i].name = try (calloc(1, MAX_USERNAME_LEN)); - players[i].kingdom = try (calloc(1, MAX_KINGDOM_LEN)); - strlcpy(players[i].name, player->name, MAX_USERNAME_LEN); - strlcpy(players[i].kingdom, player->kingdom, MAX_KINGDOM_LEN); - for (j = 2; j < LENGTH(fields); j++) - ((long *)&players[i])[j] = ((long *)player)[j]; - -} - -void -update_player(char *buf, size_t siz, Player *player, unsigned int i) +update_player(char *buf, size_t siz, Player *player, Player *new_player) { char *plto, *pltn, *pltd; - unsigned int j; - long old, new, diff; - size_t s = 0; - - /* keep this commented to not update name and keep corrected change */ - /* strlcpy(players[i].name, player->name, MAX_USERNAME_LEN); */ + size_t i, s = 0; + uint32_t old, new; + int32_t diff; s += snprintf(buf + s, siz - s, "**%s**'s profile has been updated.\n", - players[i].name); + player->name); - if (strcmp(players[i].kingdom, player->kingdom) != 0) { + if (strcmp(player->kingdom, new_player->kingdom) != 0) { s += snprintf(buf + s, siz - s, "%s: %s -> %s\n", fields[1], - players[i].kingdom, player->kingdom); - strlcpy(players[i].kingdom, player->kingdom, MAX_KINGDOM_LEN); + player->kingdom, new_player->kingdom); + strlcpy(player->kingdom, new_player->kingdom, MAX_KINGDOM_SIZ); } /* -2 to not include update and userid */ - for (j = 2; j < LENGTH(fields) - 2; j++) { + for (i = 2; i < LENGTH(fields) - 2; i++) { if (s >= siz) { log_warn("%s: string truncation", __func__); return; } - old = ((long *)&players[i])[j]; - new = ((long *)player)[j]; + old = U32CAST(player)[i]; + new = U32CAST(new_player)[i]; diff = new - old; /* * this is to prevent random tesseract error but block the user @@ -371,40 +363,40 @@ update_player(char *buf, size_t siz, Player *player, unsigned int i) if (new == 0 || diff == 0) continue; /* don't update if stat decreases except for special cases */ - if (diff < 0 && j != ASCENSION && j != GLOBAL_RANK && - j != REGIONAL_RANK && j != COMPETITIVE_RANK) + if (diff < 0 && i != ASCENSION && i != GLOBAL_RANK && + i != REGIONAL_RANK && i != COMPETITIVE_RANK) continue; - if (j == PLAYTIME) { + if (i == PLAYTIME) { plto = playtime_to_str(old); pltn = playtime_to_str(new); pltd = playtime_to_str(diff); s += snprintf(buf + s, siz - s, "%s: %s -> %s (+%s)\n", - fields[7], plto, pltn, pltd); + fields[i], plto, pltn, pltd); free(plto); free(pltn); free(pltd); } else { - s += snprintf(buf + s, siz - s, "%s: ", fields[j]); + s += snprintf(buf + s, siz - s, "%s: ", fields[i]); s += ufmt(buf + s, siz - s, old); - if (j == DISTANCE) s += strlcpy(buf + s, "m", siz - s); + if (i == DISTANCE) s += strlcpy(buf + s, "m", siz - s); s += strlcpy(buf + s, " -> ", siz - s); s += ufmt(buf + s, siz - s, new); - if (j == DISTANCE) s += strlcpy(buf + s, "m", siz - s); + if (i == DISTANCE) s += strlcpy(buf + s, "m", siz - s); s += strlcpy(buf + s, " (", siz - s); s += ifmt(buf + s, siz - s, diff); - if (j == DISTANCE) s += strlcpy(buf + s, "m", siz - s); + if (i == DISTANCE) s += strlcpy(buf + s, "m", siz - s); s += strlcpy(buf + s, ")\n", siz - s); } /* update player */ - ((long *)&players[i])[j] = new; + U32CAST(player)[i] = new; } s += snprintf(buf + s, siz - s, "\nLast update was <t:%ld:R> on <t:%ld:f>\n", - players[i].update, players[i].update); - players[i].update = player->update; + player->update, player->update); + player->update = new_player->update; if (s >= siz) { log_warn("string truncation", __func__); return; @@ -424,27 +416,27 @@ void update_file(Player *player) { FILE *w, *r; - char line[LINE_SIZE], *startuid, tmpfname[128]; + char line[LINE_SIZE], *startuid; u64snowflake userid; unsigned int i; - int found = 0; + bool found = false; - strlcpy(tmpfname, SAVE_FOLDER, sizeof(tmpfname)); - strlcat(tmpfname, "tmpfile", sizeof(tmpfname)); r = xfopen(STATS_FILE, "r"); - w = xfopen(tmpfname, "w"); + w = xfopen(SAVE_FOLDER "tmpfile", "w"); while (fgets(line, LINE_SIZE, r)) { if ((startuid = strrchr(line, DELIM)) == NULL) - errx(1, "Line \"%s\" in %s is wrong", line, STATS_FILE); - userid = strtoul(startuid + 1, NULL, 10); + die(1, "Line \"%s\" in %s is wrong", line, STATS_FILE); + userid = strtoull(startuid + 1, NULL, 10); if (userid == player->userid) { - found = 1; + found = true; fprintf(w, "%s%c", player->name, DELIM); fprintf(w, "%s%c", player->kingdom, DELIM); - for (i = 2; i < LENGTH(fields) - 1; i++) - fprintf(w, "%ld%c", ((long *)player)[i], DELIM); - fprintf(w, "%lu\n", player->userid); + for (i = 2; i < LENGTH(fields) - 2; i++) + fprintf(w, "%"PRIu32"%c", U32CAST(player)[i], + DELIM); + fprintf(w, "%ld%c", player->update, DELIM); + fprintf(w, "%"PRIu64"\n", player->userid); } else { fprintf(w, "%s", line); } @@ -452,44 +444,42 @@ update_file(Player *player) if (!found) { fprintf(w, "%s%c", player->name, DELIM); fprintf(w, "%s%c", player->kingdom, DELIM); - for (i = 2; i < LENGTH(fields) - 1; i++) - fprintf(w, "%ld%c", ((long *)player)[i], DELIM); - fprintf(w, "%lu\n", player->userid); + for (i = 2; i < LENGTH(fields) - 2; i++) + fprintf(w, "%"PRIu32"%c", U32CAST(player)[i], DELIM); + fprintf(w, "%ld%c", player->update, DELIM); + fprintf(w, "%"PRIu64"\n", player->userid); } fclose(r); fclose(w); remove(STATS_FILE); - rename(tmpfname, STATS_FILE); + rename(SAVE_FOLDER "tmpfile", STATS_FILE); } -/* update players, write the update msg and update file, returns player's index */ -unsigned int -update_players(char *buf, size_t siz, Player *player) +/* update players, write the update msg and update file, returns updated plyr */ +Player * +update_players(char *buf, size_t siz, Player *new_player) { - unsigned int i = 0; + Player *player; size_t s = 0; - while (i < nplayers && players[i].userid != player->userid) - i++; - + player = find_player(new_player->userid); s += write_quote(buf + s, siz - s); - if (i == nplayers) { /* new player */ - nplayers++; - if (nplayers > MAX_PLAYERS) - errx(1, "There is too much players (max:%d)", MAX_PLAYERS); - create_player(player, i); + if (player == NULL) { /* new player */ + player = xmemdup(new_player, sizeof(*new_player)); + player->next = player_head; + player_head = player; s += snprintf(buf + s, siz - s, "**%s** has been registrated in the database.\n", player->name); - write_info(buf + s, siz - s, &players[i]); + write_info(buf + s, siz - s, player); } else { - update_player(buf + s, siz - s, player, i); + update_player(buf + s, siz - s, player, new_player); } - update_file(&players[i]); + update_file(player); - return i; + return player; } void @@ -498,7 +488,7 @@ stats(char *buf, size_t siz, char *url, char *username, u64snowflake userid, { unsigned int i, ret; char *txt, fname[128]; - Player player; + Player local_player, *player; /* not always a jpg but idc */ snprintf(fname, sizeof(fname), "%s/%lu.jpg", IMAGES_FOLDER, userid); @@ -513,34 +503,33 @@ stats(char *buf, size_t siz, char *url, char *username, u64snowflake userid, return; } - memset(&player, 0, sizeof(player)); - player.name = username; - player.userid = userid; - player.update = time(NULL); - for_line(&player, txt); - /* txt contains player.kingdom */ + memset(&local_player, 0, sizeof(local_player)); + strlcpy(local_player.name, username, MAX_USERNAME_SIZ); + local_player.userid = userid; + local_player.update = time(NULL); + for_line(&local_player, txt); + free(txt); /* detect wrong images */ i = 2; - while (((long *)&player)[i] == 0 && i++ < LENGTH(fields) - 2); - if (i == LENGTH(fields) - 2) { - free(txt); + while (U32CAST(&local_player)[i] == 0 && i++ < LENGTH(fields) - 2); + if (i == LENGTH(fields) - 2) return; - } - if (player.kingdom == NULL) - player.kingdom = "(null)"; - - i = update_players(buf, siz, &player); - free(txt); + if (local_player.kingdom[0] == 0) + strlcpy(local_player.kingdom, "(null)", MAX_KINGDOM_SIZ); + pthread_mutex_lock(&player_mutex); + player = update_players(buf, siz, &local_player); #ifdef DEVEL UNUSED(guild_id); UNUSED(client); + UNUSED(player); #else if (guild_id == ROLE_GUILD_ID) - update_roles(client, &players[i]); + update_roles(client, player); #endif /* DEVEL */ + pthread_mutex_unlock(&player_mutex); } void @@ -571,7 +560,7 @@ on_stats_interaction(struct discord *client, json_char *attachment; log_info("%s", __func__); - attachment = try (strdup(event->data->resolved->attachments)); + attachment = xstrdup(event->data->resolved->attachments); url = strstr(attachment, "\"url\":"); if (!url) { free(attachment); @@ -1,4 +1,8 @@ +/* Copywrong © 2023 Ratakor. See LICENSE file for license details. */ + #include <sys/stat.h> +#include <errno.h> +#include <string.h> #include "nolan.h" @@ -10,13 +14,37 @@ file_exists(const char *filename) return (stat(filename, &buf) == 0); } -FILE * -xfopen(const char *filename, const char *mode) +Player * +find_player(u64snowflake userid) { - FILE *fp; + Player *pp; + + for (pp = player_head; pp && pp->userid != userid; pp = pp->next); + + return pp; +} + +void +discord_send_message(struct discord *client, u64snowflake channel_id, + const char *fmt, ...) +{ + char buf[MAX_MESSAGE_LEN]; + va_list ap; + int rv; + + va_start(ap, fmt); + rv = vsnprintf(buf, sizeof(buf), fmt, ap); + va_end(ap); + + if (rv < 0) { + log_error("%s: %s", __func__, strerror(errno)); + return; + } + + if ((size_t)rv >= sizeof(buf)) + log_warn("%s: string truncation", __func__); - if ((fp = fopen(filename, mode)) == NULL) - die(1, "fopen: '%s' [%s]", filename, mode); + struct discord_create_message msg = { .content = buf }; - return fp; + discord_create_message(client, channel_id, &msg, NULL); } |