mirror of
https://github.com/KAMKEEL/CustomNPC-Plus.git
synced 2026-11-11 14:57:35 +00:00
792 lines
28 KiB
Python
792 lines
28 KiB
Python
"""
|
|
PA Generation Rules — Single Source of Truth for Migration
|
|
==========================================================
|
|
|
|
This module defines ALL rules governing platform-abstraction (PA) generation.
|
|
Both analyze.py and scan_codebase.py import from here.
|
|
|
|
DESIGN PRINCIPLE: Every section is a flat data structure (dict, list, or scalar).
|
|
To add a new rule, just append an entry. No classes, no inheritance, no builders.
|
|
|
|
HOW TO USE:
|
|
from pa_rules import EXISTING_MAPPINGS # replaces hardcoded KNOWN_PA
|
|
from pa_rules import classify_type # get classification for any MC type
|
|
from pa_rules import get_pa_name # get semantic PA interface name
|
|
from pa_rules import get_service_target # get service routing for SERVICE types
|
|
from pa_rules import resolve_param_type # resolve MC type → PA type for params
|
|
|
|
HOW TO EXTEND:
|
|
- New PA mapping completed? → Add to EXISTING_MAPPINGS
|
|
- New type to classify? → Add to FORCE_INTERFACE, FORCE_SERVICE, FORCE_TRANSFORM, or SUPPRESS
|
|
- New semantic name? → Add to NAMING
|
|
- New service routing? → Add to SERVICE_ROUTING
|
|
- New obfuscated method name? → Add to OBFUSCATED_METHOD_NAMES
|
|
"""
|
|
|
|
import re
|
|
|
|
# ============================================================
|
|
# SECTION 1: EXISTING MAPPINGS
|
|
# Types already abstracted in platform-api.
|
|
# Used as the single source of truth for readiness scoring.
|
|
#
|
|
# Format: "net.minecraft.fully.qualified.Name": "PAInterfaceName"
|
|
#
|
|
# HOW TO UPDATE: When you complete a PA interface migration,
|
|
# add the MC FQN → PA name mapping here. Both analyze.py and
|
|
# scan_codebase.py will pick it up automatically.
|
|
# ============================================================
|
|
|
|
EXISTING_MAPPINGS = {
|
|
# --- NBT ---
|
|
"net.minecraft.nbt.NBTTagCompound": "INbt",
|
|
"net.minecraft.nbt.NBTTagList": "INbtList",
|
|
# --- Entity ---
|
|
"net.minecraft.entity.Entity": "IEntity",
|
|
"net.minecraft.entity.EntityLivingBase": "IEntityLivingBase",
|
|
"net.minecraft.entity.EntityLiving": "IEntityLiving",
|
|
"net.minecraft.entity.player.EntityPlayer": "IPlayer",
|
|
"net.minecraft.entity.player.EntityPlayerMP": "IPlayer",
|
|
# --- Item ---
|
|
"net.minecraft.item.ItemStack": "IItemStack",
|
|
# --- World ---
|
|
"net.minecraft.world.World": "IWorld",
|
|
"net.minecraft.world.WorldServer": "IWorld",
|
|
# --- Combat ---
|
|
"net.minecraft.util.DamageSource": "IDamageSource",
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 2: TYPE CLASSIFICATION
|
|
# Controls how unabstracted MC types are handled during migration.
|
|
# Every MC type falls into exactly ONE category:
|
|
#
|
|
# EXISTING — Already has a PA interface (in EXISTING_MAPPINGS)
|
|
# INTERFACE — Should become a standalone PA interface (I*.java)
|
|
# SERVICE — Methods should be added to PlatformService or a domain service
|
|
# TRANSFORM — Handled by Static-Transform regex replacement
|
|
# SUPPRESS — Should NEVER be abstracted (client-only, debug, stays in mc1710)
|
|
# AUTO — Not explicitly classified; use heuristics (surface-miner data)
|
|
#
|
|
# HOW TO ADD: Put the MC simple class name (not FQN) in the right list.
|
|
# ============================================================
|
|
|
|
# --- Auto-classification thresholds (for types not in any list) ---
|
|
# Used by the classify command when a type isn't explicitly categorized.
|
|
|
|
# If more than this fraction of a type's used methods are static → SERVICE
|
|
STATIC_THRESHOLD = 0.6
|
|
|
|
# If a type has fewer than this many unique methods used → SERVICE (not worth an interface)
|
|
MIN_METHODS_FOR_INTERFACE = 3
|
|
|
|
# If a type is used in fewer than this many files → consider suppressing
|
|
MIN_FILES_FOR_INTERFACE = 5
|
|
|
|
|
|
# --- INTERFACE: Types that MUST become standalone PA interfaces ---
|
|
# These have rich instance-method surfaces and appear as parameters/return types.
|
|
# They represent concepts that exist across MC versions.
|
|
FORCE_INTERFACE = [
|
|
# Command/Chat system
|
|
"ICommandSender", # 5 instance methods, used for command execution context
|
|
"IChatComponent", # Chat message abstraction (→ ITextComponent)
|
|
"ChatComponentText", # Concrete text component (→ ITextComponent)
|
|
"ChatComponentTranslation", # Translatable text (→ ITextComponent)
|
|
"ChatStyle", # Text formatting (→ ITextStyle)
|
|
# Geometry / Math
|
|
"AxisAlignedBB", # Bounding boxes (→ IBoundingBox)
|
|
"Vec3", # 3D vector (→ IVector3)
|
|
"MovingObjectPosition", # Raytrace result (→ IRayTraceResult)
|
|
# Resources
|
|
"ResourceLocation", # Asset/registry identifiers (→ IResourceLocation)
|
|
# Effects
|
|
"Potion", # Potion type registry (→ IPotionType)
|
|
"PotionEffect", # Active potion effect instance (→ IPotionEffect)
|
|
# Enchantment
|
|
"Enchantment", # Enchantment type (→ IEnchantment)
|
|
# Auth
|
|
"GameProfile", # Mojang auth profile (→ IGameProfile)
|
|
# NBT extension (if needed beyond INbt)
|
|
"NBTBase", # Base NBT tag (→ INbtBase, if needed)
|
|
]
|
|
|
|
|
|
# --- SERVICE: Types whose methods → PlatformService or domain service ---
|
|
# These are static utility classes or singleton accessors.
|
|
# Java 7 can't have static methods in interfaces, so these MUST go to services.
|
|
FORCE_SERVICE = [
|
|
"CompressedStreamTools", # → NBTIO: readCompressed, writeCompressed, read
|
|
"PlayerSelector", # → PlatformService: matchPlayers (1 static method)
|
|
"MinecraftServer", # → PlatformService: getServer, isDedicatedServer, worldServerForDimension
|
|
"StringTranslate", # → PlatformService: translate
|
|
"FMLCommonHandler", # → PlatformService: getSide, getEffectiveSide
|
|
"ServerConfigurationManager", # → PlatformService: getPlayerList, sendPacketToAllPlayers
|
|
"JsonToNBT", # → PlatformService: parseNbt (1 static method)
|
|
"StatCollector", # → PlatformService: translateToLocal, translateToLocalFormatted
|
|
]
|
|
|
|
|
|
# --- TRANSFORM: Types handled by regex-based Static-Transform ---
|
|
# These are annotations, enums, or trivially-replaceable patterns.
|
|
# The actual regex patterns are in TRANSFORM_PATTERNS below.
|
|
FORCE_TRANSFORM = [
|
|
"Side", # @SideOnly(Side.CLIENT) → @ClientOnly
|
|
"SideOnly", # Annotation carrier (handled by Side patterns)
|
|
"MathHelper", # MathHelper.clamp_int → ValueUtil.clampInt (pure static math)
|
|
"EnumChatFormatting", # Color codes → string constants or ITextFormatting enum
|
|
"EnumFacing", # Direction enum (if used in core)
|
|
]
|
|
|
|
|
|
# --- SUPPRESS: Types that should NEVER be abstracted ---
|
|
# Client-only, networking, debug, entity AI internals, registration.
|
|
# These stay in mc1710 forever. Don't waste time classifying them.
|
|
SUPPRESS = [
|
|
# ---- Client-only (GUI, rendering) — never goes to core ----
|
|
"GuiButton",
|
|
"GuiScreen",
|
|
"GuiTextField",
|
|
"GuiContainer",
|
|
"GuiSlot",
|
|
"Minecraft",
|
|
"FontRenderer",
|
|
"Tessellator",
|
|
"RenderHelper",
|
|
"GL11",
|
|
"GL12",
|
|
"OpenGlHelper",
|
|
"RenderGlobal",
|
|
"TextureManager",
|
|
"IIcon",
|
|
"ScaledResolution",
|
|
"RenderManager",
|
|
"Render",
|
|
"RenderLiving",
|
|
"RenderPlayer",
|
|
"EntityRenderer",
|
|
"TileEntitySpecialRenderer",
|
|
"ModelBase",
|
|
"ModelRenderer",
|
|
"ModelBiped",
|
|
"ModelPlayer",
|
|
"EnumParticleTypes",
|
|
"WorldRenderer",
|
|
"AbstractClientPlayer",
|
|
"EntityClientPlayerMP",
|
|
"ResourcePack",
|
|
"IResourcePack",
|
|
# ---- Networking — stays in mc1710 platform layer ----
|
|
"Packet",
|
|
"PacketBuffer",
|
|
"ByteBuf",
|
|
"ByteBufInputStream",
|
|
"SimpleNetworkWrapper",
|
|
"IMessage",
|
|
"IMessageHandler",
|
|
"MessageContext",
|
|
"FMLProxyPacket",
|
|
# ---- Entity AI internals — entities extend MC directly ----
|
|
"EntityAIBase",
|
|
"EntityAITasks",
|
|
"PathNavigate",
|
|
"DataWatcher",
|
|
"EntityAITarget",
|
|
"EntityAIWatchClosest",
|
|
"PathFinder",
|
|
"NavigationPath",
|
|
"PathPoint",
|
|
"PathEntity",
|
|
# ---- Debug / Internal ----
|
|
"Profiler",
|
|
"CrashReport",
|
|
"CrashReportCategory",
|
|
# ---- Forge event system — stays in mc1710 ----
|
|
"Event",
|
|
"EventBus",
|
|
"SubscribeEvent",
|
|
"ForgeEventFactory",
|
|
"MinecraftForge",
|
|
"FMLEvent",
|
|
"FMLPreInitializationEvent",
|
|
"FMLInitializationEvent",
|
|
"FMLPostInitializationEvent",
|
|
"FMLServerStartingEvent",
|
|
"FMLServerStoppingEvent",
|
|
# ---- Registration — platform-specific forever ----
|
|
"GameRegistry",
|
|
"LanguageRegistry",
|
|
"EntityRegistry",
|
|
"NetworkRegistry",
|
|
"TickRegistry",
|
|
# ---- Mixin internals ----
|
|
"SpongePowered",
|
|
"Mixin",
|
|
"Shadow",
|
|
"Inject",
|
|
# ---- Recipe system (NEVER list in AGENTS.md) ----
|
|
"ShapedRecipes",
|
|
"ShapelessRecipes",
|
|
"CraftingManager",
|
|
"IRecipe",
|
|
# ---- Block/Tile internals that stay platform-side ----
|
|
# (IBlock/ITileEntity exist but wrap ScriptedBlock, not raw MC Block)
|
|
"CreativeTabs",
|
|
"MapColor",
|
|
# ---- Inventory internals ----
|
|
"InventoryBasic",
|
|
"InventoryPlayer",
|
|
]
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 3: NAMING
|
|
# Semantic name overrides for generated PA interfaces.
|
|
# Maps MC simple class name → PA interface name.
|
|
#
|
|
# WHY: MC names are version-specific (ChatComponentText in 1.7,
|
|
# TextComponent in 1.16, Component in 1.19). PA names must be
|
|
# version-agnostic and stable across all versions.
|
|
#
|
|
# DEFAULT: If not listed here, the generated name is "I" + MCClassName.
|
|
#
|
|
# HOW TO ADD: When you decide on a semantic name for a type,
|
|
# add it here. The classify and generate-pa commands use this.
|
|
# ============================================================
|
|
|
|
NAMING = {
|
|
# ---- Chat / Text system ----
|
|
# 1.7: ChatComponentText, 1.16: StringTextComponent, 1.19: Component.literal()
|
|
"ChatComponentText": "ITextComponent",
|
|
"ChatComponentTranslation": "ITextComponent",
|
|
"ChatComponentScore": "ITextComponent",
|
|
"IChatComponent": "ITextComponent",
|
|
"ChatStyle": "ITextStyle",
|
|
"EnumChatFormatting": "ITextFormatting",
|
|
# ---- Server ----
|
|
# 1.7: MinecraftServer.getServer(), 1.13+: different access patterns
|
|
"MinecraftServer": "IServer",
|
|
"DedicatedServer": "IServer",
|
|
"IntegratedServer": "IServer",
|
|
"ServerConfigurationManager": "IPlayerManager",
|
|
# ---- Geometry ----
|
|
# 1.7: AxisAlignedBB, 1.14+: same name but different package
|
|
"AxisAlignedBB": "IBoundingBox",
|
|
"Vec3": "IVector3",
|
|
"ChunkCoordinates": "IPos", # IPos already exists in PA
|
|
"MovingObjectPosition": "IRayTraceResult",
|
|
# ---- Effects ----
|
|
# 1.7: Potion, 1.9+: different registry system but same concept
|
|
"Potion": "IPotionType",
|
|
"PotionEffect": "IPotionEffect",
|
|
# ---- Entity merges ----
|
|
# Multiple MC classes → single PA interface (wraps closest common ancestor)
|
|
"EntityPlayer": "IPlayer",
|
|
"EntityPlayerMP": "IPlayer",
|
|
"EntityLivingBase": "IEntityLivingBase",
|
|
"EntityLiving": "IEntityLiving",
|
|
"EntityCreature": "IEntityLiving",
|
|
# ---- Resources ----
|
|
# 1.7: ResourceLocation, all versions: same concept
|
|
"ResourceLocation": "IResourceLocation",
|
|
# ---- Auth ----
|
|
"GameProfile": "IGameProfile",
|
|
# ---- Enchantment ----
|
|
"Enchantment": "IEnchantment",
|
|
# ---- NBT base ----
|
|
"NBTBase": "INbtBase",
|
|
# ---- World ----
|
|
"WorldServer": "IWorld",
|
|
# ---- Command ----
|
|
"ICommandSender": "ICommandSender", # Keep same name (already good)
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 4: ROUTING
|
|
# Where generated interfaces and service methods go.
|
|
#
|
|
# HOW TO UPDATE:
|
|
# - New sub-package needed? → Add a pattern to PACKAGE_ROUTING
|
|
# - New service target? → Add to SERVICE_ROUTING
|
|
# ============================================================
|
|
|
|
# Default output locations
|
|
DEFAULT_INTERFACE_PACKAGE = "noppes.npcs.api"
|
|
DEFAULT_INTERFACE_DIR = "platform-api/src/main/java/noppes/npcs/api/"
|
|
|
|
# Sub-package routing: regex on PA interface name → sub-package
|
|
# First match wins. If nothing matches → DEFAULT_INTERFACE_PACKAGE.
|
|
PACKAGE_ROUTING = [
|
|
(r"IPlayer|IEntity|ILiving|ICustomNpc", "noppes.npcs.api.entity"),
|
|
(r"IItem", "noppes.npcs.api.item"),
|
|
(r"IScoreboard", "noppes.npcs.api.scoreboard"),
|
|
(r"IOverlay", "noppes.npcs.api.overlay"),
|
|
(r"IJob", "noppes.npcs.api.jobs"),
|
|
(r"IRole", "noppes.npcs.api.roles"),
|
|
(r"IHandler|IPlayerData|IPlayerQuest", "noppes.npcs.api.handler"),
|
|
# Everything else → default package
|
|
]
|
|
|
|
# Service routing: MC simple name → service target
|
|
# Defines WHERE service methods go for FORCE_SERVICE types.
|
|
SERVICE_ROUTING = {
|
|
"CompressedStreamTools": {
|
|
"service": "NBTIO",
|
|
"file": "platform-api/src/main/java/noppes/npcs/platform/nbt/NBTIO.java",
|
|
"reason": "NBT I/O operations belong with NBT infrastructure",
|
|
},
|
|
"PlayerSelector": {
|
|
"service": "PlatformService",
|
|
"file": "platform-api/src/main/java/kamkeel/npcs/platform/PlatformService.java",
|
|
"reason": "Single static method (matchPlayers), too small for own service",
|
|
},
|
|
"MinecraftServer": {
|
|
"service": "PlatformService",
|
|
"file": "platform-api/src/main/java/kamkeel/npcs/platform/PlatformService.java",
|
|
"reason": "Server access is a core platform concern (getServer, isDedicatedServer)",
|
|
},
|
|
"StringTranslate": {
|
|
"service": "PlatformService",
|
|
"file": "platform-api/src/main/java/kamkeel/npcs/platform/PlatformService.java",
|
|
"reason": "Translation is platform-specific (different I18n in each version)",
|
|
},
|
|
"FMLCommonHandler": {
|
|
"service": "PlatformService",
|
|
"file": "platform-api/src/main/java/kamkeel/npcs/platform/PlatformService.java",
|
|
"reason": "Side detection is platform infrastructure",
|
|
},
|
|
"ServerConfigurationManager": {
|
|
"service": "PlatformService",
|
|
"file": "platform-api/src/main/java/kamkeel/npcs/platform/PlatformService.java",
|
|
"reason": "Player management access is core platform concern",
|
|
},
|
|
"JsonToNBT": {
|
|
"service": "NBTIO",
|
|
"file": "platform-api/src/main/java/noppes/npcs/platform/nbt/NBTIO.java",
|
|
"reason": "JSON↔NBT conversion belongs with NBT infrastructure",
|
|
},
|
|
"StatCollector": {
|
|
"service": "PlatformService",
|
|
"file": "platform-api/src/main/java/kamkeel/npcs/platform/PlatformService.java",
|
|
"reason": "Translation/localization is platform-specific",
|
|
},
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 5: STATIC TRANSFORM PATTERNS
|
|
# Regex patterns for FORCE_TRANSFORM types.
|
|
# Used by Static-Transform.ps1 during migration waves.
|
|
#
|
|
# Format: "MCTypeName": [(regex_pattern, replacement), ...]
|
|
#
|
|
# HOW TO ADD: When you identify a new pattern for a TRANSFORM type,
|
|
# add the regex + replacement here. The classify command shows
|
|
# these patterns in its output.
|
|
# ============================================================
|
|
|
|
TRANSFORM_PATTERNS = {
|
|
"Side": [
|
|
(r"@SideOnly\s*\(\s*Side\.CLIENT\s*\)", "@ClientOnly"),
|
|
(r"@SideOnly\s*\(\s*Side\.SERVER\s*\)", "@ServerOnly"),
|
|
],
|
|
"SideOnly": [], # Handled by Side patterns above
|
|
"MathHelper": [
|
|
(r"MathHelper\.clamp_int\s*\(", "ValueUtil.clampInt("),
|
|
(r"MathHelper\.clamp_float\s*\(", "ValueUtil.clampFloat("),
|
|
(r"MathHelper\.clamp_double\s*\(", "ValueUtil.clampDouble("),
|
|
(r"MathHelper\.sqrt_double\s*\(", "Math.sqrt("),
|
|
(r"MathHelper\.sqrt_float\s*\(", "(float)Math.sqrt("),
|
|
(r"MathHelper\.floor_double\s*\(", "ValueUtil.floorDouble("),
|
|
(r"MathHelper\.floor_float\s*\(", "ValueUtil.floorFloat("),
|
|
],
|
|
"EnumChatFormatting": [
|
|
# Typically replaced with string constants or suppressed in core
|
|
# Add specific patterns as migration progresses
|
|
],
|
|
"EnumFacing": [
|
|
# Direction enum — add patterns when core code needs directions
|
|
],
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 6: PARAMETER TYPE RESOLUTION
|
|
# When generating PA interface method signatures, MC types in
|
|
# parameters and return types need to be resolved to PA types.
|
|
#
|
|
# Strategy for types NOT in this map:
|
|
# "object" — use Object (pragmatic, safe, fast)
|
|
# "recursive" — also generate interfaces for param types (thorough)
|
|
# ============================================================
|
|
|
|
UNRESOLVED_PARAM_STRATEGY = "object"
|
|
|
|
# Known parameter type mappings: MC simple name → PA type name
|
|
# Used when generating interface method signatures.
|
|
PARAM_TYPE_MAP = {
|
|
# ---- Already abstracted (mirrors EXISTING_MAPPINGS by simple name) ----
|
|
"EntityPlayer": "IPlayer",
|
|
"EntityPlayerMP": "IPlayer",
|
|
"NBTTagCompound": "INbt",
|
|
"NBTTagList": "INbtList",
|
|
"ItemStack": "IItemStack",
|
|
"World": "IWorld",
|
|
"WorldServer": "IWorld",
|
|
"DamageSource": "IDamageSource",
|
|
"Entity": "IEntity",
|
|
"EntityLivingBase": "IEntityLivingBase",
|
|
"EntityLiving": "IEntityLiving",
|
|
# ---- Planned abstractions (use the NAMING value) ----
|
|
"ResourceLocation": "IResourceLocation",
|
|
"ChatComponentText": "ITextComponent",
|
|
"ChatComponentTranslation": "ITextComponent",
|
|
"IChatComponent": "ITextComponent",
|
|
"AxisAlignedBB": "IBoundingBox",
|
|
"Vec3": "IVector3",
|
|
"GameProfile": "IGameProfile",
|
|
"PotionEffect": "IPotionEffect",
|
|
"Potion": "IPotionType",
|
|
"Enchantment": "IEnchantment",
|
|
"ICommandSender": "ICommandSender",
|
|
"MovingObjectPosition": "IRayTraceResult",
|
|
# ---- Java primitives (pass through unchanged) ----
|
|
"String": "String",
|
|
"int": "int",
|
|
"float": "float",
|
|
"double": "double",
|
|
"boolean": "boolean",
|
|
"long": "long",
|
|
"byte": "byte",
|
|
"short": "short",
|
|
"char": "char",
|
|
"void": "void",
|
|
"byte[]": "byte[]",
|
|
"int[]": "int[]",
|
|
"String[]": "String[]",
|
|
"float[]": "float[]",
|
|
"double[]": "double[]",
|
|
# ---- Java standard library (pass through unchanged) ----
|
|
"List": "List",
|
|
"ArrayList": "List",
|
|
"Map": "Map",
|
|
"HashMap": "Map",
|
|
"Set": "Set",
|
|
"HashSet": "Set",
|
|
"Collection": "Collection",
|
|
"File": "File",
|
|
"UUID": "UUID",
|
|
"InputStream": "InputStream",
|
|
"OutputStream": "OutputStream",
|
|
"DataInputStream": "DataInputStream",
|
|
"DataOutputStream": "DataOutputStream",
|
|
"Object": "Object",
|
|
"Runnable": "Runnable",
|
|
}
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 7: METHOD FILTERING
|
|
# Controls which methods are included/excluded in generated interfaces.
|
|
# ============================================================
|
|
|
|
# Methods to ALWAYS exclude from generated interfaces
|
|
# (inherited Object methods that never belong in PA interfaces)
|
|
EXCLUDED_METHODS = [
|
|
"toString",
|
|
"hashCode",
|
|
"equals",
|
|
"clone",
|
|
"finalize",
|
|
"getClass",
|
|
"notify",
|
|
"notifyAll",
|
|
"wait",
|
|
]
|
|
|
|
# Obfuscated MCP method names → readable names
|
|
# When surface-miner finds these, use the readable name in generated interfaces.
|
|
OBFUSCATED_METHOD_NAMES = {
|
|
"func_152457_a": "decompress",
|
|
"func_150296_c": "getTagType",
|
|
"func_150299_b": "getKeySet",
|
|
"func_74875_a": "getTagList",
|
|
"func_82580_o": "getShort",
|
|
"func_150295_c": "merge",
|
|
"func_74737_a": "copy",
|
|
"func_74732_a": "getId",
|
|
}
|
|
|
|
# If True, obfuscated methods (matching func_\d+_[a-z]) that are NOT in
|
|
# OBFUSCATED_METHOD_NAMES are excluded from generated interfaces.
|
|
EXCLUDE_UNMAPPED_OBFUSCATED = True
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 8: JAVA CONSTRAINTS
|
|
# Compile-time constraints that affect generated code shape.
|
|
# ============================================================
|
|
|
|
# Java language level for generated interfaces.
|
|
# Java 7: no default methods, no static interface methods.
|
|
# Java 8: default and static methods allowed.
|
|
JAVA_TARGET = 7
|
|
|
|
# If True, add @Override on wrapper methods implementing PA interfaces.
|
|
ADD_OVERRIDE = True
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 9: HELPER FUNCTIONS
|
|
# Convenience functions for consuming the rules above.
|
|
# These are the primary API for other modules.
|
|
# ============================================================
|
|
|
|
|
|
def _simple_name(fqn_or_simple):
|
|
"""Extract simple class name from FQN or return as-is."""
|
|
return fqn_or_simple.rsplit(".", 1)[-1] if "." in fqn_or_simple else fqn_or_simple
|
|
|
|
|
|
def is_existing(fqn):
|
|
"""Check if a fully-qualified MC type already has a PA abstraction."""
|
|
return fqn in EXISTING_MAPPINGS
|
|
|
|
|
|
def get_existing_pa(fqn):
|
|
"""Get the PA interface name for an already-abstracted MC type, or None."""
|
|
return EXISTING_MAPPINGS.get(fqn)
|
|
|
|
|
|
def classify_type(simple_name, surface_data=None):
|
|
"""
|
|
Classify an MC type into a migration category.
|
|
|
|
Args:
|
|
simple_name: MC class simple name (e.g. "MinecraftServer")
|
|
surface_data: Optional dict from surface-miner with keys:
|
|
"methods": [{"name": str, "kind": "static"|"instance", "count": int}, ...]
|
|
If provided, enables AUTO classification with heuristics.
|
|
|
|
Returns:
|
|
dict with keys:
|
|
"classification": "EXISTING"|"INTERFACE"|"SERVICE"|"TRANSFORM"|"SUPPRESS"|"AUTO"
|
|
"reason": str explaining why
|
|
"pa_name": str or None (the PA interface/service name)
|
|
"target": str or None (service file path for SERVICE types)
|
|
"transform_patterns": list or None (regex patterns for TRANSFORM types)
|
|
"""
|
|
name = _simple_name(simple_name)
|
|
|
|
# Check EXISTING first (by simple name match against EXISTING_MAPPINGS values)
|
|
for fqn, pa in EXISTING_MAPPINGS.items():
|
|
if _simple_name(fqn) == name:
|
|
return {
|
|
"classification": "EXISTING",
|
|
"reason": f"Already abstracted as {pa}",
|
|
"pa_name": pa,
|
|
"target": None,
|
|
"transform_patterns": None,
|
|
}
|
|
|
|
# Check SUPPRESS
|
|
if name in SUPPRESS:
|
|
return {
|
|
"classification": "SUPPRESS",
|
|
"reason": "Suppressed (client-only, debug, networking, or stays in mc1710)",
|
|
"pa_name": None,
|
|
"target": None,
|
|
"transform_patterns": None,
|
|
}
|
|
|
|
# Check FORCE_TRANSFORM
|
|
if name in FORCE_TRANSFORM:
|
|
patterns = TRANSFORM_PATTERNS.get(name, [])
|
|
return {
|
|
"classification": "TRANSFORM",
|
|
"reason": f"Static-Transform ({len(patterns)} regex patterns)",
|
|
"pa_name": None,
|
|
"target": None,
|
|
"transform_patterns": patterns,
|
|
}
|
|
|
|
# Check FORCE_SERVICE
|
|
if name in FORCE_SERVICE:
|
|
routing = SERVICE_ROUTING.get(name, {})
|
|
return {
|
|
"classification": "SERVICE",
|
|
"reason": routing.get("reason", "Force-classified as service"),
|
|
"pa_name": None,
|
|
"target": routing.get("file"),
|
|
"service_name": routing.get("service", "PlatformService"),
|
|
"transform_patterns": None,
|
|
}
|
|
|
|
# Check FORCE_INTERFACE
|
|
if name in FORCE_INTERFACE:
|
|
pa_name = get_pa_name(name)
|
|
return {
|
|
"classification": "INTERFACE",
|
|
"reason": "Force-classified as interface",
|
|
"pa_name": pa_name,
|
|
"target": _resolve_interface_path(pa_name),
|
|
"transform_patterns": None,
|
|
}
|
|
|
|
# AUTO classification using surface data (if provided)
|
|
if surface_data and surface_data.get("methods"):
|
|
methods = surface_data["methods"]
|
|
total = len(methods)
|
|
static_count = sum(1 for m in methods if m.get("kind") == "static")
|
|
static_ratio = static_count / total if total > 0 else 0
|
|
|
|
if total < MIN_METHODS_FOR_INTERFACE:
|
|
return {
|
|
"classification": "SERVICE",
|
|
"reason": f"Auto: only {total} methods (< {MIN_METHODS_FOR_INTERFACE} threshold)",
|
|
"pa_name": None,
|
|
"target": "platform-api/src/main/java/kamkeel/npcs/platform/PlatformService.java",
|
|
"service_name": "PlatformService",
|
|
"transform_patterns": None,
|
|
}
|
|
|
|
if static_ratio > STATIC_THRESHOLD:
|
|
return {
|
|
"classification": "SERVICE",
|
|
"reason": f"Auto: {static_ratio:.0%} static methods (> {STATIC_THRESHOLD:.0%} threshold)",
|
|
"pa_name": None,
|
|
"target": "platform-api/src/main/java/kamkeel/npcs/platform/PlatformService.java",
|
|
"service_name": "PlatformService",
|
|
"transform_patterns": None,
|
|
}
|
|
|
|
pa_name = get_pa_name(name)
|
|
return {
|
|
"classification": "INTERFACE",
|
|
"reason": f"Auto: {total} methods, {static_ratio:.0%} static → instance-heavy",
|
|
"pa_name": pa_name,
|
|
"target": _resolve_interface_path(pa_name),
|
|
"transform_patterns": None,
|
|
}
|
|
|
|
# No data, no explicit rule → UNKNOWN
|
|
return {
|
|
"classification": "AUTO",
|
|
"reason": "Not classified. Run with --surface to enable auto-classification.",
|
|
"pa_name": get_pa_name(name),
|
|
"target": None,
|
|
"transform_patterns": None,
|
|
}
|
|
|
|
|
|
def get_pa_name(simple_name):
|
|
"""
|
|
Get the PA interface name for an MC type.
|
|
Uses NAMING overrides, falls back to "I" + simple_name.
|
|
"""
|
|
name = _simple_name(simple_name)
|
|
return NAMING.get(name, f"I{name}")
|
|
|
|
|
|
def get_service_target(simple_name):
|
|
"""
|
|
Get the service routing for a SERVICE-classified type.
|
|
Returns dict with 'service', 'file', 'reason' or None.
|
|
"""
|
|
name = _simple_name(simple_name)
|
|
return SERVICE_ROUTING.get(name)
|
|
|
|
|
|
def resolve_param_type(mc_type_name):
|
|
"""
|
|
Resolve an MC type name to its PA equivalent for use in method signatures.
|
|
Returns the PA type name, or "Object" if unresolved (per UNRESOLVED_PARAM_STRATEGY).
|
|
"""
|
|
name = _simple_name(mc_type_name)
|
|
resolved = PARAM_TYPE_MAP.get(name)
|
|
if resolved:
|
|
return resolved
|
|
if UNRESOLVED_PARAM_STRATEGY == "object":
|
|
return "Object"
|
|
return name # "recursive" strategy — caller handles generation
|
|
|
|
|
|
def should_exclude_method(method_name):
|
|
"""Check if a method should be excluded from generated interfaces."""
|
|
if method_name in EXCLUDED_METHODS:
|
|
return True
|
|
if EXCLUDE_UNMAPPED_OBFUSCATED and re.match(r"func_\d+_[a-z]", method_name):
|
|
return method_name not in OBFUSCATED_METHOD_NAMES
|
|
return False
|
|
|
|
|
|
def resolve_obfuscated_method(method_name):
|
|
"""Resolve an obfuscated MCP method name to readable, or return as-is."""
|
|
return OBFUSCATED_METHOD_NAMES.get(method_name, method_name)
|
|
|
|
|
|
def get_interface_package(pa_name):
|
|
"""Determine which package a PA interface should go in based on its name."""
|
|
for pattern, package in PACKAGE_ROUTING:
|
|
if re.search(pattern, pa_name):
|
|
return package
|
|
return DEFAULT_INTERFACE_PACKAGE
|
|
|
|
|
|
def _resolve_interface_path(pa_name):
|
|
"""Build the full file path for a PA interface."""
|
|
package = get_interface_package(pa_name)
|
|
package_path = package.replace(".", "/")
|
|
return f"platform-api/src/main/java/{package_path}/{pa_name}.java"
|
|
|
|
|
|
# ============================================================
|
|
# SECTION 10: QUICK REFERENCE
|
|
# Summary of all classified types for fast lookup.
|
|
# Run: python -m pa_rules to see the full classification table.
|
|
# ============================================================
|
|
|
|
if __name__ == "__main__":
|
|
print("=" * 72)
|
|
print("PA GENERATION RULES — CLASSIFICATION SUMMARY")
|
|
print("=" * 72)
|
|
|
|
print(f"\n EXISTING ({len(EXISTING_MAPPINGS)} types):")
|
|
for fqn, pa in sorted(EXISTING_MAPPINGS.items(), key=lambda x: x[1]):
|
|
print(f" {_simple_name(fqn):30s} → {pa}")
|
|
|
|
print(f"\n INTERFACE ({len(FORCE_INTERFACE)} types):")
|
|
for t in sorted(FORCE_INTERFACE):
|
|
pa = get_pa_name(t)
|
|
path = _resolve_interface_path(pa)
|
|
print(f" {t:30s} → {pa:20s} {path}")
|
|
|
|
print(f"\n SERVICE ({len(FORCE_SERVICE)} types):")
|
|
for t in sorted(FORCE_SERVICE):
|
|
routing = SERVICE_ROUTING.get(t, {})
|
|
svc = routing.get("service", "PlatformService")
|
|
print(f" {t:30s} → {svc}")
|
|
|
|
print(f"\n TRANSFORM ({len(FORCE_TRANSFORM)} types):")
|
|
for t in sorted(FORCE_TRANSFORM):
|
|
patterns = TRANSFORM_PATTERNS.get(t, [])
|
|
print(f" {t:30s} ({len(patterns)} patterns)")
|
|
|
|
print(f"\n SUPPRESS ({len(SUPPRESS)} types):")
|
|
for i in range(0, len(SUPPRESS), 6):
|
|
chunk = SUPPRESS[i : i + 6]
|
|
print(f" {', '.join(chunk)}")
|
|
|
|
total = (
|
|
len(EXISTING_MAPPINGS)
|
|
+ len(FORCE_INTERFACE)
|
|
+ len(FORCE_SERVICE)
|
|
+ len(FORCE_TRANSFORM)
|
|
+ len(SUPPRESS)
|
|
)
|
|
print(f"\n TOTAL CLASSIFIED: {total}")
|
|
print(f" REMAINING AUTO: {400 - total} (of ~400 MC types in codebase)")
|
|
print(f"\n Run 'python analyze.py classify <type>' for per-type classification.")
|