Advertisement
BillyGalbreath

1.13 Custom Entity Registration (Replace a vanilla Entity)

Jul 27th, 2018
765
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 6.58 KB | None | 0 0
  1.     @Override
  2.     public void onLoad() {
  3.         // ok, first thing we do is make a new EntityTypes for the custom entity
  4.         EntityTypes.a<EntityRidableDolphin> type = EntityTypes.a.a(EntityRidableDolphin.class, (Function<? super World, ? extends EntityRidableDolphin>) EntityRidableDolphin::new);
  5.         EntityTypes<EntityRidableDolphin> types = type.a("dolphin");
  6.  
  7.  
  8.         // now we manually add this EntityTypes to the REGISTRY
  9.         // this replaces the EntityTypes.REGISTRY.a(key, EntityTypes) call
  10.         MinecraftKey key = new MinecraftKey("dolphin");
  11.         try {
  12.             // this is only present on Paper servers, so we surround in a try/catch so we dont crash Spigot servers
  13.             EntityTypes.clsToKeyMap.put(EntityRidableDolphin.class, key);
  14.             EntityTypes.clsToTypeMap.put(EntityRidableDolphin.class, EntityType.fromName("dolphin"));
  15.         } catch (NoSuchFieldError ignore) {
  16.         }
  17.  
  18.  
  19.         // now we have to use a lot of reflection to hack our way through the actual registry
  20.         // The registry has 3 arrays that it uses to lookup EntityTypes from ID and ID from EntityTypes.
  21.         // The "b" field holds the EntityTypes in a specific index depending on the hash of the object.
  22.         // The "c" field holds the ID for "d" field using the same index as "b" (confused yet?)
  23.         // The "d" field holds the EntityType at a "squished" index (all null objects moved to the end)
  24.         // See https://pastebin.com/mBp8f9uz for an example of these fields and what I mean.
  25.         try {
  26.             // the "a" field is the RegistryID containing all the EntityTypes in memory at indexes based on the object's hash
  27.             Field registryMaterials_fieldA = RegistryMaterials.class.getDeclaredField("a");
  28.             registryMaterials_fieldA.setAccessible(true);
  29.             RegistryID<EntityTypes<?>> registryID = (RegistryID<EntityTypes<?>>) registryMaterials_fieldA.get(EntityTypes.REGISTRY);
  30.  
  31.             // this is the internal ID for the current dolphin for the "d" field, lets keep new one in the same spot
  32.             int originalID = registryID.getId(EntityTypes.DOLPHIN);
  33.  
  34.             // Calculate the new id for "b" and "c" using the object's hash
  35.             Method registryID_methodD = RegistryID.class.getDeclaredMethod("d", Object.class);
  36.             registryID_methodD.setAccessible(true);
  37.             Method registryID_methodE = RegistryID.class.getDeclaredMethod("e", int.class);
  38.             registryID_methodE.setAccessible(true);
  39.             int newIndex = (int) registryID_methodE.invoke(registryID, registryID_methodD.invoke(registryID, types));
  40.  
  41.             // null out the original EntityType and add in the new one in the correct spot
  42.             Field registryID_fieldB = RegistryID.class.getDeclaredField("b");
  43.             registryID_fieldB.setAccessible(true);
  44.             Object[] arrB = (Object[]) registryID_fieldB.get(registryID);
  45.             arrB[newIndex] = types;
  46.             int oldIndex = -1;
  47.             for (int i = 0; i < arrB.length; i++) {
  48.                 if (arrB[i] == EntityTypes.DOLPHIN) {
  49.                     arrB[i] = null;
  50.                     oldIndex = i;
  51.                     break;
  52.                 }
  53.             }
  54.             registryID_fieldB.set(registryID, arrB);
  55.  
  56.             // zero out the old "b" location and set the new one
  57.             Field registryID_fieldC = RegistryID.class.getDeclaredField("c");
  58.             registryID_fieldC.setAccessible(true);
  59.             int[] arrC = (int[]) registryID_fieldC.get(registryID);
  60.             arrC[oldIndex] = 0;
  61.             arrC[newIndex] = originalID;
  62.             registryID_fieldC.set(registryID, arrC);
  63.  
  64.             // update "d" field with the new EntityType
  65.             Field registryID_fieldD = RegistryID.class.getDeclaredField("d");
  66.             registryID_fieldD.setAccessible(true);
  67.             Object[] arrD = (Object[]) registryID_fieldD.get(registryID);
  68.             arrD[originalID] = types;
  69.             registryID_fieldD.set(registryID, arrD);
  70.  
  71.             // set the 3 arrays back to the "a" field
  72.             registryMaterials_fieldA.set(EntityTypes.REGISTRY, registryID);
  73.  
  74.             // this is a simple inversed (backwards) map for EntityTypes by MinecraftKey
  75.             Field registryId_b = RegistryMaterials.class.getDeclaredField("b");
  76.             registryId_b.setAccessible(true);
  77.             Map<EntityTypes<?>, MinecraftKey> mapB_original = (Map<EntityTypes<?>, MinecraftKey>) registryId_b.get(EntityTypes.REGISTRY);
  78.             Map<EntityTypes<?>, MinecraftKey> mapB_replacement = HashBiMap.create();
  79.             for (Map.Entry<EntityTypes<?>, MinecraftKey> entry : mapB_original.entrySet()) {
  80.                 if (entry.getKey() != EntityTypes.DOLPHIN) {
  81.                     mapB_replacement.put(entry.getKey(), entry.getValue());
  82.                 } else {
  83.                     mapB_replacement.put(types, key);
  84.                 }
  85.             }
  86.             registryId_b.set(EntityTypes.REGISTRY, mapB_replacement);
  87.  
  88.             // this is a simple map for EntityTypes by MinecraftKey
  89.             Field registrySimple_fieldC = RegistrySimple.class.getDeclaredField("c");
  90.             registrySimple_fieldC.setAccessible(true);
  91.             Map<MinecraftKey, EntityTypes<?>> mapC = (Map<MinecraftKey, EntityTypes<?>>) registrySimple_fieldC.get(EntityTypes.REGISTRY);
  92.             mapC.put(key, types);
  93.             registrySimple_fieldC.set(EntityTypes.REGISTRY, mapC);
  94.  
  95.             // This replaces the constant field in EntityTypes with the custom one
  96.             Field entityTypes_fieldDOLPHIN = EntityTypes.class.getField("DOLPHIN");
  97.             entityTypes_fieldDOLPHIN.setAccessible(true);
  98.             Field modifiersField = Field.class.getDeclaredField("modifiers");
  99.             modifiersField.setAccessible(true);
  100.             modifiersField.setInt(entityTypes_fieldDOLPHIN, entityTypes_fieldDOLPHIN.getModifiers() & ~Modifier.FINAL);
  101.             entityTypes_fieldDOLPHIN.set(null, types);
  102.         } catch (NoSuchFieldException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) {
  103.             e.printStackTrace();
  104.         }
  105.  
  106.         // this fixes the spawner egg to point to the correct EntityTypes.
  107.         Item dolphinSpawnEgg = CraftItemStack.asNMSCopy(new ItemStack(Material.DOLPHIN_SPAWN_EGG)).getItem();
  108.         try {
  109.             Field field_d = ItemMonsterEgg.class.getDeclaredField("d");
  110.             field_d.setAccessible(true);
  111.             field_d.set(dolphinSpawnEgg, EntityTypes.DOLPHIN);
  112.         } catch (NoSuchFieldException | IllegalAccessException e) {
  113.             e.printStackTrace();
  114.         }
  115.  
  116.     }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement