Skip to content

Instantly share code, notes, and snippets.

@ilyakurdyukov
Last active September 8, 2025 17:12
Show Gist options
  • Save ilyakurdyukov/fdaa24e0c7c97394c607973d22a83850 to your computer and use it in GitHub Desktop.
Save ilyakurdyukov/fdaa24e0c7c97394c607973d22a83850 to your computer and use it in GitHub Desktop.
A small library for changing the speed of time in games for Linux.
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <dlfcn.h>
#define HOOK(ret, name, args) ret name args { \
static ret (*old_##name) args = 0; \
if (!old_##name) { \
old_##name = (ret (*) args) dlsym(RTLD_NEXT, #name); \
if (!old_##name) { printf("dlsym \"%s\" failed\n", #name); exit(1); } \
}
#include <time.h>
#include <sys/time.h>
static double speed, rspeed;
static int64_t old_timeval;
__attribute__((constructor))
static void speed_init() {
const char *str = getenv("HOOK_SPEED");
{
struct timeval tv;
speed = rspeed = 1;
gettimeofday(&tv, NULL);
old_timeval = tv.tv_sec * (int64_t)1000000 + tv.tv_usec;
rspeed = 1 / (speed = 0.5);
}
if (str) {
double newspeed = atof(str);
printf("HOOK_SPEED = %f\n", newspeed);
if (newspeed == newspeed && newspeed >= 0.1 && newspeed <= 2.0) {
rspeed = 1 / (speed = newspeed);
} else {
printf("HOOK_SPEED out of range, set to default %f\n", speed);
}
}
}
static void change_timeval(const struct timeval *tv, struct timeval *new, double mul) {
int64_t n = 1000000, t = tv->tv_sec * n + tv->tv_usec;
t -= old_timeval;
t *= mul;
t += old_timeval;
new->tv_usec = t % n;
new->tv_sec = t / n;
}
static void change_timespec(const struct timespec *tp, struct timespec *new, double mul) {
int64_t n = 1000000000, t = tp->tv_sec * n + tp->tv_nsec;
t *= mul;
new->tv_nsec = t % n;
new->tv_sec = t / n;
}
HOOK(int, gettimeofday, (struct timeval *tv, void *tz))
int ret = old_gettimeofday(tv, tz);
if (!ret && tv) change_timeval(tv, tv, speed);
return ret;
}
HOOK(int, clock_gettime, (clockid_t clk_id, struct timespec *tp))
int ret = old_clock_gettime(clk_id, tp);
if (!ret && tp) change_timespec(tp, tp, speed);
return ret;
}
HOOK(int, nanosleep, (const struct timespec *req, struct timespec *rem))
struct timespec req2, rem2;
if (req) change_timespec(req, &req2, rspeed), req = &req2;
int ret = old_nanosleep(req, rem);
if (!ret && rem) change_timespec(rem, &rem2, speed);
return ret;
}
@ilyakurdyukov
Copy link
Author

Build:

cc -Os libtimehook.c -o libtimehook.so -fPIC -shared -ldl

Usage:

export HOOK_SPEED=0.65
export LD_PRELOAD="path/to/libtimehook.so"
./game

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment