aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRatakor <ratakor@disroot.org>2023-07-31 15:07:42 +0200
committerRatakor <ratakor@disroot.org>2023-07-31 15:07:42 +0200
commit8b703c3f480a1d80d176a77d8ed07595c16f638f (patch)
tree479f73ae25d45a4c36a15ba70c4c7dcf8a771e3a
parent8c5a7f8ed77c6f6be2bd240fe83a787edb681a57 (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--Makefile6
-rw-r--r--README.md11
-rw-r--r--config.def.h3
-rw-r--r--libre/LICENSE13
-rw-r--r--libre/Makefile45
-rw-r--r--libre/inc/dalloc.h59
-rw-r--r--libre/inc/evil.h63
-rw-r--r--libre/inc/ubik.h45
-rw-r--r--libre/libre.h8
-rw-r--r--libre/src/dalloc.c393
-rw-r--r--libre/src/ubik.c228
-rw-r--r--src/Makefile13
-rw-r--r--src/cmd_correct.c56
-rw-r--r--src/cmd_info.c35
-rw-r--r--src/cmd_lbraid.c7
-rw-r--r--src/cmd_leaderboard.c162
-rw-r--r--src/cmd_source.c77
-rw-r--r--src/init.c43
-rw-r--r--src/main.c23
-rw-r--r--src/nolan.h83
-rw-r--r--src/ocr.c42
-rw-r--r--src/raids.c61
-rw-r--r--src/roles.c2
-rw-r--r--src/stats.c253
-rw-r--r--src/util.c40
25 files changed, 499 insertions, 1272 deletions
diff --git a/Makefile b/Makefile
index 39ea9d2..e8887ab 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/README.md b/README.md
index 7b00de1..687d2ab 100644
--- a/README.md
+++ b/README.md
@@ -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,
diff --git a/src/init.c b/src/init.c
index 096de3e..2550257 100644
--- a/src/init.c
+++ b/src/init.c
@@ -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);
}
diff --git a/src/main.c b/src/main.c
index 70741ef..67e8f3d 100644
--- a/src/main.c
+++ b/src/main.c
@@ -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 */
diff --git a/src/ocr.c b/src/ocr.c
index a98c41a..0a53637 100644
--- a/src/ocr.c
+++ b/src/ocr.c
@@ -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);
diff --git a/src/util.c b/src/util.c
index db87674..4a13d47 100644
--- a/src/util.c
+++ b/src/util.c
@@ -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);
}