@@ -54,6 +54,164 @@ public static File getSeedmapperLootDir()
5454 return new File (new File (user , ".minecraft" ), "seedmapper/loot" );
5555 }
5656
57+ private static File tryLocateSeedmapperLootDir ()
58+ {
59+ Set <File > searchRoots = new LinkedHashSet <>();
60+ try
61+ {
62+ if (WurstClient .MC != null && WurstClient .MC .gameDirectory != null )
63+ addRootAndParents (searchRoots , WurstClient .MC .gameDirectory , 4 );
64+ }catch (Throwable ignored )
65+ {}
66+
67+ String user = System .getProperty ("user.home" );
68+ if (user != null && !user .isBlank ())
69+ {
70+ File home = new File (user );
71+ addRootAndParents (searchRoots , home , 0 );
72+ addRootAndParents (searchRoots , new File (home , ".minecraft" ), 0 );
73+ }
74+
75+ String appdata = System .getenv ("APPDATA" );
76+ if (appdata != null && !appdata .isBlank ())
77+ addRootAndParents (searchRoots , new File (appdata , ".minecraft" ), 0 );
78+
79+ // Also check common locations used by flatpak / other launchers
80+ if (user != null && !user .isBlank ())
81+ {
82+ File varApp = new File (user , ".var/app" );
83+ if (varApp .exists () && varApp .isDirectory ())
84+ {
85+ File [] vendors = varApp .listFiles (File ::isDirectory );
86+ if (vendors != null )
87+ {
88+ for (File vendor : vendors )
89+ {
90+ // look inside vendor/data and vendor/data/* for
91+ // SeedMapper folders
92+ File data = new File (vendor , "data" );
93+ if (data .exists () && data .isDirectory ())
94+ {
95+ File found = searchTreeForSeedmapper (data , 4 );
96+ if (found != null )
97+ return found ;
98+ }
99+ }
100+ }
101+ }
102+
103+ // common user-level locations used by some launchers
104+ File localShare = new File (user , ".local/share" );
105+ if (localShare .exists () && localShare .isDirectory ())
106+ {
107+ File found = searchTreeForSeedmapper (localShare , 3 );
108+ if (found != null )
109+ return found ;
110+ }
111+
112+ File config = new File (user , ".config" );
113+ if (config .exists () && config .isDirectory ())
114+ {
115+ File found = searchTreeForSeedmapper (config , 3 );
116+ if (found != null )
117+ return found ;
118+ }
119+
120+ }
121+
122+ for (File root : searchRoots )
123+ {
124+ File resolved = resolveSeedmapperLoot (root );
125+ if (resolved != null )
126+ return resolved ;
127+ }
128+
129+ // No matches under known roots.
130+ return null ;
131+ }
132+
133+ private static void addRootAndParents (Set <File > set , File start ,
134+ int maxDepth )
135+ {
136+ File current = start ;
137+ for (int depth = 0 ; current != null && depth <= maxDepth ; depth ++)
138+ {
139+ set .add (current );
140+ current = current .getParentFile ();
141+ }
142+ }
143+
144+ private static File resolveSeedmapperLoot (File root )
145+ {
146+ if (root == null || !root .exists () || !root .isDirectory ())
147+ return null ;
148+
149+ if (root .getName ().equalsIgnoreCase ("loot" ))
150+ return root ;
151+
152+ if (root .getName ().equalsIgnoreCase ("seedmapper" ))
153+ {
154+ File loot = new File (root , "loot" );
155+ if (loot .exists () && loot .isDirectory ())
156+ return loot ;
157+ }
158+
159+ String [] folderNames = {"seedmapper" , "SeedMapper" , "Seedmapper" };
160+ for (String folder : folderNames )
161+ {
162+ File loot = new File (new File (root , folder ), "loot" );
163+ if (loot .exists () && loot .isDirectory ())
164+ return loot ;
165+ }
166+
167+ File [] matches = root .listFiles (file -> file .isDirectory ()
168+ && file .getName ().equalsIgnoreCase ("seedmapper" ));
169+ if (matches != null )
170+ {
171+ for (File match : matches )
172+ {
173+ File loot = new File (match , "loot" );
174+ if (loot .exists () && loot .isDirectory ())
175+ return loot ;
176+ }
177+ }
178+
179+ return null ;
180+ }
181+
182+ /**
183+ * Search a directory tree up to the given depth for a SeedMapper loot
184+ * folder.
185+ */
186+ private static File searchTreeForSeedmapper (File base , int maxDepth )
187+ {
188+ if (base == null || !base .exists () || !base .isDirectory ())
189+ return null ;
190+ java .util .ArrayDeque <File > dq = new java .util .ArrayDeque <>();
191+ java .util .ArrayDeque <Integer > depth = new java .util .ArrayDeque <>();
192+ dq .add (base );
193+ depth .add (0 );
194+ while (!dq .isEmpty ())
195+ {
196+ File cur = dq .removeFirst ();
197+ int d = depth .removeFirst ();
198+ File resolved = resolveSeedmapperLoot (cur );
199+ if (resolved != null )
200+ return resolved ;
201+ if (d >= maxDepth )
202+ continue ;
203+ File [] children = cur .listFiles (File ::isDirectory );
204+ if (children == null )
205+ continue ;
206+ for (File c : children )
207+ {
208+ dq .addLast (c );
209+ depth .addLast (d + 1 );
210+ }
211+ }
212+ return null ;
213+ }
214+
57215 public static File findFileForServer (String serverIp )
58216 {
59217 if (serverIp == null )
@@ -66,24 +224,35 @@ public static File findFileForServer(String serverIp)
66224 dir .listFiles ((d , name ) -> name .toLowerCase ().endsWith (".json" ));
67225 if (files == null )
68226 return null ;
69- // Build candidate tokens to try: raw, host-only, and colon->underscore
227+ // Build candidate tokens based on SeedMapper's serverId generation
228+ // serverId = serverIp with non-alnum/dot/dash/underscore -> '_',
229+ // collapse '_' repeats,
230+ // trim leading/trailing '-' or '_' and fallback to "local" when blank.
70231 java .util .List <String > candidates = new java .util .ArrayList <>();
71- candidates .add (serverIp );
72- if (serverIp .contains (":" ))
73- {
74- candidates .add (serverIp .replace (':' , '_' ));
75- String host = serverIp .split (":" , 2 )[0 ];
76- candidates .add (host );
77- }else
78- {
79- // also add variant without trailing port-like suffix after
80- // underscore
81- int u = serverIp .indexOf ('_' );
82- if (u > 0 )
232+ try
233+ {
234+ String full = serverIp ;
235+ if (full == null )
236+ full = "" ;
237+ String sanitizedFull = full .replaceAll ("[^A-Za-z0-9._-]" , "_" )
238+ .replaceAll ("_+" , "_" ).replaceAll ("^[-_]+|[-_]+$" , "" );
239+ if (sanitizedFull .isBlank ())
240+ sanitizedFull = "local" ;
241+ candidates .add (sanitizedFull );
242+
243+ // Also add host-only sanitized (strip port) for extra tolerance
244+ if (full .contains (":" ))
83245 {
84- candidates .add (serverIp .substring (0 , u ));
246+ String host = full .split (":" , 2 )[0 ];
247+ String sanitizedHost = host .replaceAll ("[^A-Za-z0-9._-]" , "_" )
248+ .replaceAll ("_+" , "_" ).replaceAll ("^[-_]+|[-_]+$" , "" );
249+ if (sanitizedHost .isBlank ())
250+ sanitizedHost = "local" ;
251+ if (!sanitizedHost .equals (sanitizedFull ))
252+ candidates .add (sanitizedHost );
85253 }
86- }
254+ }catch (Throwable ignored )
255+ {}
87256 for (File f : files )
88257 {
89258 String name = f .getName ();
0 commit comments