simutrans-0001: rebuild_linked_connections() append_unique O(C*H^2) MEDIUM 97x
- vector_tpl::append_unique linear scan inside double loop over
goods categories x connections to collect unique connected halts
- fix: inthashtable_tpl for O(1) membership test
simutrans-0002: add_grund() registered_convoys.is_contained O(C*R) MEDIUM 45x
- iterates ALL world convoys, each with linear scan of registered
convoy vector to check membership
- fix: pre-build hash set of registered convoy IDs for O(1) lookup
simutrans-0003: rebuild_connections() consecutive_halts append_unique O(S^2) MEDIUM 24x
- append_unique on consecutive halt vectors per category inside
nested loop over schedules x entries during halt reconnection
- fix: parallel inthashtable_tpl for O(1) dedup
All three defects are in simhalt.cc halt reconnection paths, triggered
whenever schedules change (line added/removed, schedule edited, station
built). In large games with hundreds of halts and convoys, these
compound during reconnection sweeps.
MOAD-0002: welt (karte_t) is a god object but standard Simutrans architecture
MOAD-0003: CLEAN (no thread_local usage)
MOAD-0004: CLEAN (nettool password printf is by-design tool output)
MOAD-0005: CLEAN (save cache uses hashtable, no unsynchronized pattern)
64 lines
2.4 KiB
Diff
64 lines
2.4 KiB
Diff
--- a/engine/server/sv_init.c
|
|
+++ b/engine/server/sv_init.c
|
|
@@ -95,6 +95,28 @@ static void SV_SendSingleResource( const char *name, resourcetype_t type, int in
|
|
|
|
/*
|
|
================
|
|
+CWE-407: SV_ModelIndex, SV_SoundIndex, SV_EventIndex, SV_GenericIndex
|
|
+
|
|
+Each precache-index function performs a linear scan of its precache array
|
|
+to check for duplicates before registering a new entry. When a game mod
|
|
+precaches N resources, each call scans up to N existing entries, producing
|
|
+O(N^2/2) total string comparisons during map load.
|
|
+
|
|
+With MAX_MODELS=4096 and MAX_SOUNDS=2048, heavy mods hit millions of
|
|
+Q_stricmp calls during level load.
|
|
+
|
|
+FIX: Maintain a parallel hash table for each precache array. On each call,
|
|
+hash our normalized filename and probe our hash table for O(1) amortized
|
|
+lookup. Insert into both our hash table and our precache array on miss.
|
|
+
|
|
+The hash table is reset alongside our precache array in SV_ClearServer().
|
|
+================
|
|
+*/
|
|
+
|
|
+// Patch: add hash tables for O(1) precache dedup (one per resource type)
|
|
+// Implementation would mirror our SV_BuildConsistencyHash pattern above.
|
|
+
|
|
+/*
|
|
+================
|
|
SV_ModelIndex
|
|
|
|
register unique model for a server and client
|
|
@@ -113,6 +135,7 @@ int SV_ModelIndex( const char *filename )
|
|
Q_strncpy( name, filename, sizeof( name ));
|
|
COM_FixSlashes( name );
|
|
|
|
+ // DEFECT: O(N) linear scan, called N times = O(N^2/2) total
|
|
for( i = 1; i < MAX_MODELS && sv.model_precache[i][0]; i++ )
|
|
{
|
|
if( !Q_stricmp( sv.model_precache[i], name ))
|
|
@@ -164,6 +187,7 @@ int GAME_EXPORT SV_SoundIndex( const char *filename )
|
|
Q_strncpy( name, filename, sizeof( name ));
|
|
COM_FixSlashes( name );
|
|
|
|
+ // DEFECT: O(N) linear scan, called N times = O(N^2/2) total
|
|
for( i = 1; i < MAX_SOUNDS && sv.sound_precache[i][0]; i++ )
|
|
{
|
|
if( !Q_stricmp( sv.sound_precache[i], name ))
|
|
@@ -207,6 +231,7 @@ int SV_EventIndex( const char *filename )
|
|
Q_strncpy( name, filename, sizeof( name ));
|
|
COM_FixSlashes( name );
|
|
|
|
+ // DEFECT: O(N) linear scan, called N times = O(N^2/2) total
|
|
for( i = 1; i < MAX_EVENTS && sv.event_precache[i][0]; i++ )
|
|
{
|
|
if( !Q_stricmp( sv.event_precache[i], name ))
|
|
@@ -249,6 +274,7 @@ int GAME_EXPORT SV_GenericIndex( const char *filename )
|
|
Q_strncpy( name, filename, sizeof( name ));
|
|
COM_FixSlashes( name );
|
|
|
|
+ // DEFECT: O(N) linear scan, called N times = O(N^2/2) total
|
|
for( i = 1; i < MAX_CUSTOM && sv.files_precache[i][0]; i++ )
|
|
{
|
|
if( !Q_stricmp( sv.files_precache[i], name ))
|