- cmap_offset was calculated on every compose_glyph_name() call;
calculate it once with a static function.
- Drop parse_id's G_ auto-populate: an unbracketed lookup allocated
the index with no matching free. Linear-scan instead when absent.
- compose_glyph_name: require bufsz >= BUFSZ, build names with
bounded Snprintf instead of Strcpy/Strcat, drop the dead memchr.
- parse_id's permonst scan used i <= pm_count, reading one past the
SYM_MON block (S_nothing) and matching it as a monster; use <.
- Drop a stale NO_GLYPH empty-bucket comment from the open-addressed
table.
The directories and permissions portion of the linux.500 and macOS.500
hints files and their included files has been consolidated to
dirs-perms.500.
The builder can edit that one file now, to identify
the folders that will be utilised as part of the build.
Alternatively, you can set those folders and permissions in a
make.perms file in the top of the NetHack folder tree and
they should take precedence over the ones in dirs-perms.500
because dirs-perms.500 uses '?=' variable assignment, which
means "set the value of the variable if no value has been set."
* NOTE: BUILD CHANGE *
This also makes WANT_SOURCE_INSTALL=1 the default over
WANT_SHARED_INSTALL=1, if neither is explicitly set.
The new default is the safer and less-impacting default,
but it will change where things get installed over earlier
Makefile builds. You can be explicit with WANT_SHARED_INSTALL=1
in your make command to get that..
These are the differences between the two:
make WANT_SHARED_INSTALL=1 Place the results of the install/update portion
of the build into a shared area on a multiuser
system.
make WANT_SOURCE_INSTALL=1 Place the results of the install/update portion
of the build into a subfolder of the source
tree, rather than in a system-wide shared area.
Also note that the macOS hints file behaves slightly differntly depending
on whether WANT_SOURCE_INSTALL=1 was set versus letting it be the default.
That's not new, it behaved that way before.
Be able to carry out uplifts during minor release
lifetimes.
Document a way to be able to uplift struct content
without incrementing EDITLEVEL and breaking existing savefiles.
Use the mechanics outlined to uplift the contents instead, where
it is feasible to do so. The uplift is currently one-way only. An
uplifted savefile cannot be used with an earlier build of NetHack
, one built with a lower SAVEFILE_REVISION_LEVEL, than the one which
wrote the savefile.
The final byte (byte 79) of the 80 critical bytes in the savefile,
of which 10 are reserved for future expansion and not currently
used, will now be used for holding the savefile revision level
(SAVEFILE_REVISION_LEVEL in include/patchlevel.h) at the time
the savefile was written.
That leaves 9 of the bytes available for future use.
The previous rotate-1-xor put consecutive characters' bits in
adjacent positions, so short similar names like fox/bat or
jaguar/lichen collided -- 164 colliding buckets across the 9577
named glyphs. Rotate-5 spreads each character across a 5-bit
window: zero true collisions, one m68k ROL.L, no multiplication.
The 16 remaining same-hash buckets are name duplicates from a
separate compose_glyph_name bug, addressed by its own fix.
parse_id's glyph_is_object branch only emitted the "piletop_" prefix
when glyph_is_normal_piletop_obj(glyph) was true. Piletop-generic
objects (the GLYPH_OBJ_PILETOP_OFF + 1 .. + LAST_GENERIC range) hit
glyph_is_piletop_generic_obj() instead and got no prefix, so they
produced the same canonical name as their non-piletop generic
counterparts.
For example glyph 3449 (GLYPH_OBJ_OFF + GENERIC_STRANGE) and glyph
7993 (GLYPH_OBJ_PILETOP_OFF + GENERIC_STRANGE) both yielded
"G_generic_strange". 14 such pairs exist; the piletop variant is
unreachable by name from nethackrc, and the runtime hashtable's
"assume no id occurs twice" populate loop silently dropped them.
Emit "piletop_" for both piletop predicates so each glyph gets a
distinct canonical name. --dumpglyphnames now shows the 14
"G_piletop_generic_*" entries (and the count goes from 9577 to 9591;
the existing off-by-one in glyph_is_normal_piletop_obj still hides
slot GLYPH_OBJ_PILETOP_OFF + FIRST_OBJECT - 1, which is addressed by
its own fix).
The open-addressed glyphname_hashtable stored each canonical
"G_xxx" name as a dupstr'd string in its bucket: ~9577 small heap
allocations and ~290 KB of resident name strings, on top of ~256 KB
of 32768-bucket scaffolding kept at <50% load for probe performance.
On classic Mac OS the populate cost (quadratic small-allocation in a
fragmented Memory Manager heap) dominated startup time -- many
seconds on an SE/30 -- and ~800 KB resident is meaningful on the
small machines the port targets.
Switch to a sorted (hash, glyph) index sized exactly to the number
of named glyphs:
struct glyphname_hashtable_entry_t {
uint32 hash;
int glyphnum;
};
populate_glyphname_hashtable() allocates one block of MAX_GLYPH
entries (no per-name strings), fills it via compose_glyph_name() +
glyph_hash(), and qsort()s ascending by hash. Lookup binary-searches
the hash column, then walks any equal-hash neighbours verifying each
candidate by reconstructing its canonical name and strcmpi'ing it
back -- collisions are rare with 9577 uniformly-distributed 32-bit
hashes.
Other changes that fall out:
* Extract compose_glyph_name() from parse_id's bulk-iteration switch
so it is the single source of truth for "glyph number -> canonical
name". Called by find_glyph_in_hashtable for collision
verification, by populate_glyphname_hashtable, by the
--dumpglyphnames path, and by wizcustom_glyphnames.
* empty_glyphname_hashtable() reduces to free(ptr); no per-entry
strings to release.
* Drop find_glyphname_in_hashtable_by_glyphnum (no longer used --
wizcustom_glyphnames iterates compose_glyph_name directly).
* Drop the res_fill_hashtable parse_id mode; populate iterates
directly.
Memory drops from ~800 KB to ~75 KB. One allocation instead of
~9578. Lookup goes from O(1) to O(log N) but N ~ 9577 means ~14
comparisons per probe -- well under what the upstream cache ever
cost in practice.
Function names (populate_glyphname_hashtable, etc.) are kept for
extern.h compatibility; the data structure is now a sorted index,
"hashtable" in the names is historical.
--dumpglyphnames output is byte-identical.
glyph_is_normal_object includes its boundary slot
GLYPH_OBJ_OFF + FIRST_OBJECT - 1 via >=, but its piletop sibling
glyph_is_normal_piletop_obj used > and excluded the matching
GLYPH_OBJ_PILETOP_OFF + FIRST_OBJECT - 1 slot. That leaves
exactly one glyph (the would-be G_piletop_generic_venom) matching
neither the piletop-generic nor the piletop-normal predicate, so
parse_id never builds a name for it and --dumpglyphnames emits a
blank line for the slot.
Change > to >= so the two ranges are inclusive on the same side.
--dumpglyphnames now produces (8009) G_piletop_generic_venom.
Noticed this when testing a level with some barren trees which were set
in the special level to not contain bees; the barren trees are still
able to produce a low buzzing. This may convince players that they can
get bees from the tree if only they kick it enough times, which will not
happen.
To avoid that, only print this message when the tree can release bees,
augmenting the existing check for killer bees being non-extinct.
Two methods are now provided for slow ports/platforms that
need to do this for performance reasons.
Hopefully, this will avoid proliferation of more platform-specific
conditional code within initoptions_init().
Method (1): #define DISABLE_GLYPHID_CACHE_PREFILL in platform/OS's
include/*conf.h.
or
Method (2): set gd.disable_glyphid_cache_prefill = TRUE in startup code
after decl_global_init(), and prior to initoptions_init().
It has to be done after decl_global_init() because
decl_global_init() sets the value to its initialization
default.
- adjust the surface name in prompts (resolves a TODO in the code).
- be more player-friendly with the prompting, and don't prompt a
second time if the floor/surface is the only tip-destination, as
that can be annoyng and viewed as unnecessary. Instead, include
that information in the first decision prompt.
Resolves#1537
Add ANY_INT16 to any_types and use it for u.ux/uy/tx/ty and uz
dlevel/dnum. These are coordxy (int16_t) but the Lua bindings were
treating them as 1-byte fields, so on big-endian m68k Lua read the
high byte (0) instead of the actual value. Symptom: place_object
off map <0,0> in the tutorial.