mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-13 23:04:08 +00:00
Loaded all .d.ts files in assets/modId/api directory of ALL loaded mods
This commit is contained in:
1 parent
0d1520d951
commit
b86c0284ac
2 files changed
+298
-24
No files matched your search
+197
@@ -0,0 +1,197 @@
|
||||
package noppes.npcs.client.gui.util.script.interpreter.js_parser;
|
||||
|
||||
import cpw.mods.fml.common.Loader;
|
||||
import cpw.mods.fml.common.ModContainer;
|
||||
|
||||
import java.io.*;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
final class DtsModScanner {
|
||||
|
||||
private static final Pattern MOD_DTS_PATH_PATTERN = Pattern.compile("^assets/([^/]+)/api/(.+\\.d\\.ts)$");
|
||||
|
||||
private static final List<String> DOMAIN_PRIORITY = Arrays.asList("customnpcs", "npcdbc");
|
||||
|
||||
private DtsModScanner() {}
|
||||
|
||||
static List<DtsFileRef> collectDtsFilesFromMods() {
|
||||
List<DtsFileRef> dtsFiles = new ArrayList<>();
|
||||
for (ModContainer mod : Loader.instance().getModList()) {
|
||||
File source = mod.getSource();
|
||||
if (source == null || !source.exists()) {
|
||||
continue;
|
||||
}
|
||||
if (source.isDirectory()) {
|
||||
scanDirectoryForModDts(source, mod.getModId(), dtsFiles);
|
||||
} else if (source.getName().endsWith(".jar") || source.getName().endsWith(".zip")) {
|
||||
scanJarForModDts(source, mod.getModId(), dtsFiles);
|
||||
}
|
||||
}
|
||||
return dtsFiles;
|
||||
}
|
||||
|
||||
static void sortDtsFiles(List<DtsFileRef> dtsFiles) {
|
||||
Collections.sort(dtsFiles, new DtsFileRefComparator());
|
||||
}
|
||||
|
||||
static void logSummary(List<DtsFileRef> dtsFiles) {
|
||||
Map<String, Integer> domainCounts = new LinkedHashMap<>();
|
||||
for (DtsFileRef ref : dtsFiles) {
|
||||
domainCounts.put(ref.getDomain(), domainCounts.getOrDefault(ref.getDomain(), 0) + 1);
|
||||
}
|
||||
System.out.println("[JSTypeRegistry] Found " + dtsFiles.size() + " .d.ts files across " + domainCounts.size() + " domains");
|
||||
for (Map.Entry<String, Integer> entry : domainCounts.entrySet()) {
|
||||
System.out.println("[JSTypeRegistry] Domain " + entry.getKey() + ": " + entry.getValue() + " files");
|
||||
}
|
||||
}
|
||||
|
||||
private static void scanDirectoryForModDts(File baseDir, String modId, List<DtsFileRef> dtsFiles) {
|
||||
scanDirectoryForModDts(baseDir, baseDir, modId, dtsFiles);
|
||||
}
|
||||
|
||||
private static void scanDirectoryForModDts(File baseDir, File directory, String modId, List<DtsFileRef> dtsFiles) {
|
||||
File[] files = directory.listFiles();
|
||||
if (files == null) {
|
||||
return;
|
||||
}
|
||||
for (File file : files) {
|
||||
if (file.isDirectory()) {
|
||||
scanDirectoryForModDts(baseDir, file, modId, dtsFiles);
|
||||
continue;
|
||||
}
|
||||
if (!file.getName().endsWith(".d.ts")) {
|
||||
continue;
|
||||
}
|
||||
String relativePath = baseDir.toURI().relativize(file.toURI()).getPath();
|
||||
if (relativePath == null) {
|
||||
continue;
|
||||
}
|
||||
String normalized = relativePath.replace('\\', '/');
|
||||
Matcher matcher = MOD_DTS_PATH_PATTERN.matcher(normalized);
|
||||
if (matcher.matches()) {
|
||||
String domain = matcher.group(1);
|
||||
String apiPath = matcher.group(2);
|
||||
dtsFiles.add(DtsFileRef.forFile(modId, domain, apiPath, file));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void scanJarForModDts(File jarFile, String modId, List<DtsFileRef> dtsFiles) {
|
||||
try {
|
||||
JarFile jar = new JarFile(jarFile);
|
||||
Enumeration<JarEntry> entries = jar.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
String entryName = entry.getName();
|
||||
Matcher matcher = MOD_DTS_PATH_PATTERN.matcher(entryName);
|
||||
if (matcher.matches()) {
|
||||
String domain = matcher.group(1);
|
||||
String apiPath = matcher.group(2);
|
||||
dtsFiles.add(DtsFileRef.forJar(modId, domain, apiPath, jarFile, entryName));
|
||||
}
|
||||
}
|
||||
jar.close();
|
||||
} catch (Exception e) {
|
||||
System.err.println("[JSTypeRegistry] Error scanning mod jar for .d.ts files: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
static final class DtsFileRef {
|
||||
private final String modId;
|
||||
private final String domain;
|
||||
private final String relativePath;
|
||||
private final File file;
|
||||
private final File jarFile;
|
||||
private final String jarEntryName;
|
||||
|
||||
private DtsFileRef(String modId, String domain, String relativePath, File file, File jarFile, String jarEntryName) {
|
||||
this.modId = modId;
|
||||
this.domain = domain;
|
||||
this.relativePath = relativePath;
|
||||
this.file = file;
|
||||
this.jarFile = jarFile;
|
||||
this.jarEntryName = jarEntryName;
|
||||
}
|
||||
|
||||
static DtsFileRef forFile(String modId, String domain, String relativePath, File file) {
|
||||
return new DtsFileRef(modId, domain, relativePath, file, null, null);
|
||||
}
|
||||
|
||||
static DtsFileRef forJar(String modId, String domain, String relativePath, File jarFile, String jarEntryName) {
|
||||
return new DtsFileRef(modId, domain, relativePath, null, jarFile, jarEntryName);
|
||||
}
|
||||
|
||||
InputStream openStream() throws IOException {
|
||||
if (file != null) {
|
||||
return new FileInputStream(file);
|
||||
}
|
||||
if (jarFile != null && jarEntryName != null) {
|
||||
final JarFile jar = new JarFile(jarFile);
|
||||
JarEntry entry = jar.getJarEntry(jarEntryName);
|
||||
if (entry == null) {
|
||||
jar.close();
|
||||
return null;
|
||||
}
|
||||
InputStream is = jar.getInputStream(entry);
|
||||
return new FilterInputStream(is) {
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
super.close();
|
||||
jar.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
String getDomain() {
|
||||
return domain;
|
||||
}
|
||||
|
||||
String getRelativePath() {
|
||||
return relativePath;
|
||||
}
|
||||
|
||||
String getOrigin() {
|
||||
return modId + ":" + domain + ":" + relativePath;
|
||||
}
|
||||
}
|
||||
|
||||
private static class DtsFileRefComparator implements Comparator<DtsFileRef> {
|
||||
@Override
|
||||
public int compare(DtsFileRef a, DtsFileRef b) {
|
||||
int domainCompare = Integer.compare(getDomainRank(a.getDomain()), getDomainRank(b.getDomain()));
|
||||
if (domainCompare != 0) {
|
||||
return domainCompare;
|
||||
}
|
||||
int domainNameCompare = a.getDomain().compareTo(b.getDomain());
|
||||
if (domainNameCompare != 0) {
|
||||
return domainNameCompare;
|
||||
}
|
||||
int filePriorityCompare = Integer.compare(getFilePriority(a.getRelativePath()), getFilePriority(b.getRelativePath()));
|
||||
if (filePriorityCompare != 0) {
|
||||
return filePriorityCompare;
|
||||
}
|
||||
return a.getRelativePath().compareTo(b.getRelativePath());
|
||||
}
|
||||
|
||||
private int getDomainRank(String domain) {
|
||||
int idx = DOMAIN_PRIORITY.indexOf(domain);
|
||||
return idx >= 0 ? idx : DOMAIN_PRIORITY.size();
|
||||
}
|
||||
|
||||
private int getFilePriority(String relativePath) {
|
||||
if (relativePath.endsWith("hooks.d.ts")) {
|
||||
return 0;
|
||||
}
|
||||
if (relativePath.endsWith("index.d.ts")) {
|
||||
return 1;
|
||||
}
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
+101
-24
@@ -6,6 +6,7 @@ import noppes.npcs.scripted.NpcAPI;
|
||||
|
||||
import java.io.*;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.*;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
@@ -18,9 +19,6 @@ public class JSTypeRegistry {
|
||||
|
||||
private static JSTypeRegistry INSTANCE;
|
||||
|
||||
// Resource location for the API definitions
|
||||
private static final String API_RESOURCE_PATH = "customnpcs:api/";
|
||||
|
||||
// All registered types by full name (e.g., "IPlayerEvent.InteractEvent")
|
||||
private final Map<String, JSTypeInfo> types = new LinkedHashMap<>();
|
||||
|
||||
@@ -29,6 +27,12 @@ public class JSTypeRegistry {
|
||||
|
||||
// Type aliases (simple name -> full type name)
|
||||
private final Map<String, String> typeAliases = new HashMap<>();
|
||||
|
||||
// Type full name -> origin string (modId:domain:relativePath)
|
||||
private final Map<String, String> typeOrigins = new HashMap<>();
|
||||
|
||||
// Alias name -> origin string (modId:domain:relativePath)
|
||||
private final Map<String, String> aliasOrigins = new HashMap<>();
|
||||
|
||||
// Context-aware hook function signatures: namespace -> functionName -> list of signatures
|
||||
// The namespace is the event interface name (e.g., "INpcEvent", "IPlayerEvent")
|
||||
@@ -53,6 +57,8 @@ public class JSTypeRegistry {
|
||||
|
||||
private boolean initialized = false;
|
||||
private boolean initializationAttempted = false;
|
||||
|
||||
private String currentSource = null;
|
||||
|
||||
public static JSTypeRegistry getInstance() {
|
||||
if (INSTANCE == null) {
|
||||
@@ -73,25 +79,28 @@ public class JSTypeRegistry {
|
||||
|
||||
try {
|
||||
TypeScriptDefinitionParser parser = new TypeScriptDefinitionParser(this);
|
||||
|
||||
// Recursively load all .d.ts files from the api directory
|
||||
Set<String> dtsFiles = findAllDtsFilesInResources("assets/customnpcs/api");
|
||||
|
||||
System.out.println("[JSTypeRegistry] Found " + dtsFiles.size() + " .d.ts files in resources");
|
||||
|
||||
// Load hooks.d.ts and index.d.ts first if they exist (defines core types)
|
||||
if (dtsFiles.contains("hooks.d.ts")) {
|
||||
loadResourceFile(parser, "hooks.d.ts");
|
||||
}
|
||||
if (dtsFiles.contains("index.d.ts")) {
|
||||
loadResourceFile(parser, "index.d.ts");
|
||||
}
|
||||
|
||||
// Load all other .d.ts files
|
||||
for (String filePath : dtsFiles) {
|
||||
if (!filePath.equals("hooks.d.ts") && !filePath.equals("index.d.ts")) {
|
||||
loadResourceFile(parser, filePath);
|
||||
|
||||
List<DtsModScanner.DtsFileRef> dtsFiles = DtsModScanner.collectDtsFilesFromMods();
|
||||
if (dtsFiles.isEmpty()) {
|
||||
Set<String> fallbackFiles = findAllDtsFilesInResources("assets/customnpcs/api");
|
||||
|
||||
System.out.println("[JSTypeRegistry] Found " + fallbackFiles.size() + " .d.ts files in resources");
|
||||
|
||||
if (fallbackFiles.contains("hooks.d.ts")) {
|
||||
loadResourceFile(parser, "hooks.d.ts");
|
||||
}
|
||||
if (fallbackFiles.contains("index.d.ts")) {
|
||||
loadResourceFile(parser, "index.d.ts");
|
||||
}
|
||||
|
||||
for (String filePath : fallbackFiles) {
|
||||
if (!filePath.equals("hooks.d.ts") && !filePath.equals("index.d.ts")) {
|
||||
loadResourceFile(parser, filePath);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
DtsModScanner.logSummary(dtsFiles);
|
||||
loadDtsFiles(parser, dtsFiles);
|
||||
}
|
||||
|
||||
// Phase 2: Resolve all type parameters now that all types are loaded
|
||||
@@ -113,6 +122,33 @@ public class JSTypeRegistry {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private void loadDtsFiles(TypeScriptDefinitionParser parser, List<DtsModScanner.DtsFileRef> dtsFiles) {
|
||||
DtsModScanner.sortDtsFiles(dtsFiles);
|
||||
for (DtsModScanner.DtsFileRef ref : dtsFiles) {
|
||||
try (InputStream is = ref.openStream()) {
|
||||
if (is == null) {
|
||||
continue;
|
||||
}
|
||||
String content = readFully(new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)));
|
||||
setCurrentSource(ref.getOrigin());
|
||||
parser.parseDefinitionFile(content, ref.getRelativePath());
|
||||
} catch (Exception e) {
|
||||
System.err.println("[JSTypeRegistry] Failed to load " + ref.getOrigin() + ": " + e.getMessage());
|
||||
} finally {
|
||||
setCurrentSource(null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private String readFully(BufferedReader reader) throws IOException {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
sb.append(line).append("\n");
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively find all .d.ts files in the resources directory.
|
||||
@@ -210,7 +246,7 @@ public class JSTypeRegistry {
|
||||
System.err.println("[JSTypeRegistry] Error scanning JAR for .d.ts files: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Load a specific .d.ts file from resources.
|
||||
*/
|
||||
@@ -280,9 +316,20 @@ public class JSTypeRegistry {
|
||||
* Register a type.
|
||||
*/
|
||||
public void registerType(JSTypeInfo type) {
|
||||
types.put(type.getFullName(), type);
|
||||
String fullName = type.getFullName();
|
||||
if (types.containsKey(fullName)) {
|
||||
logTypeCollision(fullName, getCurrentSource());
|
||||
return;
|
||||
}
|
||||
types.put(fullName, type);
|
||||
typeOrigins.put(fullName, getCurrentSource());
|
||||
if (type.getJavaFqn() != null && !type.getJavaFqn().isEmpty()) {
|
||||
typesByJavaFqn.put(type.getJavaFqn(), type);
|
||||
String javaFqn = type.getJavaFqn();
|
||||
if (!typesByJavaFqn.containsKey(javaFqn)) {
|
||||
typesByJavaFqn.put(javaFqn, type);
|
||||
} else {
|
||||
System.out.println("[JSTypeRegistry] Duplicate javaFqn " + javaFqn + " from " + getCurrentSource());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +345,15 @@ public class JSTypeRegistry {
|
||||
* Register a type alias.
|
||||
*/
|
||||
public void registerTypeAlias(String alias, String fullType) {
|
||||
if (typeAliases.containsKey(alias)) {
|
||||
String existing = typeAliases.get(alias);
|
||||
if (!Objects.equals(existing, fullType)) {
|
||||
logAliasCollision(alias, existing, fullType, getCurrentSource());
|
||||
}
|
||||
return;
|
||||
}
|
||||
typeAliases.put(alias, fullType);
|
||||
aliasOrigins.put(alias, getCurrentSource());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -755,13 +810,35 @@ public class JSTypeRegistry {
|
||||
*/
|
||||
public void clear() {
|
||||
types.clear();
|
||||
typesByJavaFqn.clear();
|
||||
typeAliases.clear();
|
||||
typeOrigins.clear();
|
||||
aliasOrigins.clear();
|
||||
hooks.clear();
|
||||
contextHooks.clear();
|
||||
globalEngineObjects.clear();
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
private void setCurrentSource(String source) {
|
||||
currentSource = source;
|
||||
}
|
||||
|
||||
private String getCurrentSource() {
|
||||
return currentSource == null ? "unknown" : currentSource;
|
||||
}
|
||||
|
||||
private void logTypeCollision(String fullName, String incomingSource) {
|
||||
String existingSource = typeOrigins.getOrDefault(fullName, "unknown");
|
||||
System.out.println("[JSTypeRegistry] Duplicate type " + fullName + " from " + incomingSource + " (kept " + existingSource + ")");
|
||||
}
|
||||
|
||||
private void logAliasCollision(String alias, String existingType, String incomingType, String incomingSource) {
|
||||
String existingSource = aliasOrigins.getOrDefault(alias, "unknown");
|
||||
System.out.println("[JSTypeRegistry] Duplicate alias " + alias + " from " + incomingSource + " (kept " + existingSource + ")");
|
||||
System.out.println("[JSTypeRegistry] Alias " + alias + " existing=" + existingType + " incoming=" + incomingType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Represents a hook function signature with its namespace.
|
||||
*/
|
||||
|
||||
Reference in new issue
Block a user