add support for a unique game identifier

A 37 character field for holding a unique identifier is added
to the save file, as well as to an ancestor field in bones files.

Since it requires installation of libuuid package on Linux,
it requires an exlicit WANT_NHUUID=1 on the Make command line there.
Without the libuuid support, the saved nhuuid is empty, which should
be harmless.

This also moves the save and restore of gm.moves higher up so that it
already has a value the first time it is used against a relative saved
timestamp.

Invalidates savefiles and bones files due to new fields.

I need to get this committed before I can test out the macOS portion,
so there may be some build issues there briefly (hopefully), and
follow-up commits to resolve them.

Increments EDITLEVEL.
This commit is contained in:
nhmall
2026-04-25 14:48:49 -04:00
parent 5b66ba4c99
commit 309afb2eb2
17 changed files with 150 additions and 5 deletions
+2
View File
@@ -1174,6 +1174,8 @@ struct instance_globals_saved_m {
struct instance_globals_saved_n {
/* dungeon.c */
int n_dgns; /* number of dungeons (also used in mklev.c and do.c) */
/* files.c */
char nhuuid[37];
/* mkroom.c */
int nroom;
/* region.c */
+4
View File
@@ -1128,6 +1128,8 @@ extern boolean Death_quote(char *, int) NONNULLARG1;
extern void livelog_add(long ll_type, const char *) NONNULLARG2;
ATTRNORETURN extern void do_deferred_showpaths(int) NORETURN;
extern boolean contains_directory(const char *);
extern void get_nhuuid(void);
extern void free_nhuuid(void);
/* ### fountain.c ### */
@@ -4031,6 +4033,8 @@ extern char *get_port_id(char *);
#ifdef RUNTIME_PASTEBUF_SUPPORT
extern void port_insert_pastebuf(char *);
#endif
extern void get_nhuuid(void);
extern void free_nhuuid(void);
#endif /* !MAKEDEFS_C && !MDLIB_C */
+1 -1
View File
@@ -17,7 +17,7 @@
* Incrementing EDITLEVEL can be used to force invalidation of old bones
* and save files.
*/
#define EDITLEVEL 140
#define EDITLEVEL 141
/*
* Development status possibilities.
+1
View File
@@ -780,6 +780,7 @@ newgame(void)
svc.context.next_attrib_check = 600L; /* arbitrary first setting */
svc.context.tribute.enabled = TRUE; /* turn on 3.6 tributes */
svc.context.tribute.tributesz = sizeof(struct tribute_info);
get_nhuuid();
for (i = LOW_PM; i < NUMMONS; i++)
svm.mvitals[i].mvflags = mons[i].geno & G_NOCORPSE;
+4
View File
@@ -611,6 +611,7 @@ savebones(int how, time_t when, struct obj *corpse)
nhfp->mode = WRITING;
store_version(nhfp);
Sfo_char(nhfp, &svn.nhuuid[0], "ancestor-nhuuid", sizeof svn.nhuuid);
/* if a bones pool digit is in use, it precedes the bonesid
string and isn't recorded in the file */
Sfo_char(nhfp, &c, "bones_count", 1);
@@ -632,6 +633,7 @@ getbones(void)
NHFILE *nhfp = (NHFILE *) 0;
char c = 0, *bonesid,
oldbonesid[40] = { 0 }; /* was [10]; more should be safer */
char ancestor_nhuuid[SIZE(svn.nhuuid)];
#ifndef SFCTOOL
if (discover) /* save bones files for real games */
@@ -670,6 +672,8 @@ getbones(void)
return 0;
}
}
Sfi_char(nhfp, &ancestor_nhuuid[0], "ancestor-nhuuid",
sizeof ancestor_nhuuid);
Sfi_char(nhfp, &c, "bones_count", 1); /* length incl. '\0' */
if ((unsigned) c <= sizeof oldbonesid) {
Sfi_char(nhfp, oldbonesid, "bonesid", (int) c);
+5
View File
@@ -929,6 +929,11 @@ static const struct instance_globals_saved_m init_svm = {
static const struct instance_globals_saved_n init_svn = {
/* dungeon.c */
0, /* n_dgns */
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0
}, /* nhuuid */
/* mkroom.c */
0, /* nroom */
/* region.c */
+1
View File
@@ -1529,6 +1529,7 @@ free_saved_games(char **saved)
free((genericptr_t) saved);
}
}
#endif /* !SFCTOOL */
/* ---------- END SAVE FILE HANDLING ----------- */
+2 -1
View File
@@ -537,6 +537,8 @@ restgamestate(NHFILE *nhfp)
#endif
Sfi_ulong(nhfp, &uid, "gamestate-uid");
Sfi_char(nhfp, &svn.nhuuid[0], "nhuuid", sizeof svn.nhuuid);
Sfi_long(nhfp, &svm.moves, "gamestate-moves");
#ifndef SFCTOOL
if (SYSOPT_CHECK_SAVE_UID
&& uid != (unsigned long) getuid()) { /* strange ... */
@@ -676,7 +678,6 @@ restgamestate(NHFILE *nhfp)
restore_dungeon(nhfp);
restlevchn(nhfp);
Sfi_long(nhfp, &svm.moves, "gamestate-moves");
/* hero_seq isn't saved and restored because it can be recalculated */
gh.hero_seq = svm.moves << 3; /* normally handled in moveloop() */
Sfi_q_score(nhfp, &svq.quest_status, "gamestate-quest_status");
+3 -1
View File
@@ -292,6 +292,8 @@ savegamestate(NHFILE *nhfp)
program_state.saving++; /* caller should/did already set this... */
uid = (unsigned long) getuid();
Sfo_ulong(nhfp, &uid, "gamestate-uid");
Sfo_char(nhfp, &svn.nhuuid[0], "nhuuid", sizeof svn.nhuuid);
Sfo_long(nhfp, &svm.moves, "gamestate-moves");
moves_to_relative_time(&svc.context.seer_turn);
moves_to_relative_time(&svc.context.digging.lastdigtime);
Sfo_context_info(nhfp, &svc.context, "gamestate-context");
@@ -322,7 +324,6 @@ savegamestate(NHFILE *nhfp)
save_dungeon(nhfp, (boolean) !!update_file(nhfp),
(boolean) !!release_data(nhfp));
savelevchn(nhfp);
Sfo_long(nhfp, &svm.moves, "gamestate-moves");
Sfo_q_score(nhfp, &svq.quest_status, "gamestate-quest_status");
for (i = 0; i < (MAXSPELL + 1); ++i) {
Sfo_spell(nhfp, &svs.spl_book[i], "gamestate-spl_book");
@@ -1177,6 +1178,7 @@ freedynamicdata(void)
release_runtime_info(); /* build-time options and version stuff */
free_convert_filenames();
#endif /* FREE_ALL_MEMORY */
free_nhuuid();
if (VIA_WINDOWPORT())
status_finish();
+24
View File
@@ -143,6 +143,30 @@ append_slash(char *name)
return;
}
void
get_nhuuid(void)
{
unsigned char stmp[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if (svn.nhuuid[0])
return;
/* FIXME: fill in a useful valie UUID somehow */
Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", (char *) stmp);
}
void
free_nhuuid(void)
{
int i;
for (i = 0; i < SIZE(svn.nhuuid); i++) {
svn.nhuuid[i] = 0;
}
}
void
getreturn(const char *str)
{
+4
View File
@@ -168,6 +168,10 @@ CPLUSPLUS_NEED_DEPSUPPRESS = 1
endif # WANT_WIN_QT6
endif # WANT_WIN_QT
ifdef WANT_NHUUID
USE_UUIDLIB=1
endif
ifeq "$(GIT)" "1"
ifndef GITSUBMODULES
GITSUBMODULES=1
+4
View File
@@ -227,6 +227,10 @@ ifdef MAKEFILE_SRC
ifdef CURSESLIB
WINLIB += $(CURSESLIB)
endif #CURSESLIB
ifdef WANT_NHUUID
CFLAGS+= -DNHUUID
LIBS+=-luuid
endif #NHUUID
endif #MAKEFILE_SRC
ifdef WANT_WIN_X11
+38
View File
@@ -15,6 +15,13 @@
#include <fcntl.h>
#endif
#ifdef NHUUID
/* for uuid */
#ifdef LINUX
#include <uuid/uuid.h>
#endif
#endif /* NHUUID */
#if !defined(_BULL_SOURCE) && !defined(__sgi) && !defined(_M_UNIX)
#if !defined(SUNOS4) && !(defined(ULTRIX) && defined(__GNUC__))
#if defined(POSIX_TYPES) || defined(SVR4) || defined(HPUX)
@@ -833,5 +840,36 @@ sys_random_seed(void)
}
return seed;
}
void
get_nhuuid(void)
{
char struuid[37] = { 0 };
#if defined(LINUX) && defined(NHUUID)
uuid_t binuuid;
#endif
#if defined(MACOS) && defined(NHUUID)
extern char *get_mac_uuid(char **); /* sys/unix/macuuid.m */
#endif
if (svn.nhuuid[0])
return;
#if defined(MACOS) && defined(NHUUID)
get_mac_uuid(&struuid[0]);
#elif defined(LINUX) && defined(NHUUID)
uuid_generate_random(binuuid);
uuid_unparse(binuuid, struuid);
#endif /* MACOS || LINUX */
Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", &struuid[0]);
}
void
free_nhuuid(void)
{
int i;
for (i = 0; i < SIZE(svn.nhuuid); i++) {
svn.nhuuid[i] = 0;
}
}
/*unixmain.c*/
+24
View File
@@ -513,4 +513,28 @@ sys_random_seed(void)
return seed;
}
void
get_nhuuid(void)
{
unsigned char stmp[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
if (svn.nhuuid[0])
return;
/* FIXME: fill in a useful valie UUID somehow */
Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", (char *) stmp);
}
void
free_nhuuid(void)
{
int i;
for (i = 0; i < SIZE(svn.nhuuid); i++) {
svn.nhuuid[i] = 0;
}
}
/*vmsmain.c*/
+1 -1
View File
@@ -78,7 +78,7 @@
<LanguageStandard_C>stdclatest</LanguageStandard_C>
</ClCompile>
<Link>
<AdditionalDependencies>hacklib.lib;lualib.lib;kernel32.lib;dbghelp.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;UserEnv.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>hacklib.lib;lualib.lib;kernel32.lib;dbghelp.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;winmm.lib;Winmm.lib;UserEnv.lib;Rpcrt4.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>$(SndWavDir);$(SysWindDir);%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
+1 -1
View File
@@ -100,7 +100,7 @@
</ResourceCompile>
<Link>
<SubSystem>Windows</SubSystem>
<AdditionalDependencies>hacklib.lib;lualib.lib;dbghelp.lib;comctl32.lib;winmm.lib;UserEnv.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>hacklib.lib;lualib.lib;dbghelp.lib;comctl32.lib;winmm.lib;UserEnv.lib;Rpcrt4.lib;bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
<Manifest>
<AdditionalManifestFiles>$(WinWin32Dir)NethackW.exe.manifest;%(AdditionalManifestFiles)</AdditionalManifestFiles>
+31
View File
@@ -28,6 +28,7 @@
#include <errno.h>
#ifdef WIN32
#include <rpc.h>
#include <VersionHelpers.h>
#include <UserEnv.h>
@@ -530,6 +531,36 @@ nethack_exit(int code)
exit(code);
}
void
get_nhuuid(void)
{
UUID binuuid;
unsigned char *stmp;
RPC_STATUS rpcstatus;
if (svn.nhuuid[0])
return;
rpcstatus = UuidCreate(&binuuid);
if (rpcstatus == RPC_S_OK) {
rpcstatus = UuidToStringA(&binuuid, &stmp);
if (rpcstatus == RPC_S_OK) {
Snprintf(svn.nhuuid, sizeof svn.nhuuid, "%s", (char *) stmp);
RpcStringFree(&stmp);
}
}
}
void
free_nhuuid(void)
{
int i;
for (i = 0; i < SIZE(svn.nhuuid); i++) {
svn.nhuuid[i] = 0;
}
}
#ifdef WIN32CON
#undef kbhit
#include <conio.h>