aboutsummaryrefslogtreecommitdiff
path: root/featureClass/forgeEvents.js
blob: e852c49bc5a012bd8401523b0fcfe43ff5370417 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
importClass(net.minecraftforge.common.MinecraftForge) //i would have used the ct module but it is broken (line 78) (this is fixed verison)
importPackage(net.minecraftforge.fml.common.eventhandler)
importPackage(org.objectweb.asm)
importClass(java.lang.ClassLoader)
importClass(org.apache.commons.lang3.RandomStringUtils)
importClass(java.util.function.Consumer)

const L = s => `L${s};`

const LoadedInsts = []

function defineClassBytes(name, bytes) {
  const classLoader = Packages.com.chattriggers.ctjs.CTJS.class.getClassLoader()

  const defClass = ClassLoader.class.getDeclaredMethods()[23] // defineClass()

  defClass.setAccessible(true)

  const n = new java.lang.String(name)
  const o = new java.lang.Integer(0)
  const s = new java.lang.Integer(bytes.length)
  return defClass.invoke(classLoader, n, bytes, o, s)
}

const registerForge = (e, cb) => {
  const cw = new ClassWriter(0)

  const event = Type.getType(e.class).internalName
  const name = RandomStringUtils.randomAlphabetic(7)

  const consumer = Type.getType(Consumer.class).internalName
  const mcForge = Type.getType(MinecraftForge.class).internalName
  const eventBus = Type.getType(EventBus.class).internalName
  const subscribeEvent = Type.getType(SubscribeEvent.class).internalName
  const obj = Type.getType(java.lang.Object.class).internalName

  cw.visit(Opcodes.V1_8, Opcodes.ACC_PUBLIC, name, null, obj, null)
  // cw.visitInnerClass("net/minecraftforge/event/entity/player/PlayerEvent$BreakSpeed", "net/minecraftforge/event/entity/player/PlayerEvent", "BreakSpeed", ACC_PUBLIC + ACC_STATIC);
  {
    cw.visitField(Opcodes.ACC_PRIVATE + Opcodes.ACC_FINAL, "callback", L(consumer), L(consumer + "<" + L(event) + ">"), null).visitEnd()
  }
  {
    const con = cw.visitMethod(Opcodes.ACC_PUBLIC, "<init>", "(" + L(consumer) + ")V", "(" + L(consumer + "<" + L(event) + ">") + ")V", null)
    con.visitCode()
    con.visitVarInsn(Opcodes.ALOAD, 0)
    con.visitMethodInsn(Opcodes.INVOKESPECIAL, obj, "<init>", "()V", false)
    
    con.visitVarInsn(Opcodes.ALOAD, 0)
    con.visitVarInsn(Opcodes.ALOAD, 1)
    con.visitFieldInsn(Opcodes.PUTFIELD, name, "callback", L(consumer))
    con.visitFieldInsn(Opcodes.GETSTATIC, mcForge, "EVENT_BUS", L(eventBus))
    con.visitVarInsn(Opcodes.ALOAD, 0)
    con.visitMethodInsn(Opcodes.INVOKEVIRTUAL, eventBus, "register", "(" + L(obj) + ")V", false)
    con.visitInsn(Opcodes.RETURN)
    con.visitMaxs(2, 2)
    con.visitEnd()
  }
  {
    const mv = cw.visitMethod(Opcodes.ACC_PUBLIC, "on", "(" + L(event) + ")V", null, null)
    {
      const av = mv.visitAnnotation(L(subscribeEvent), true)
      av.visitEnd()
    }
    mv.visitCode()
    mv.visitVarInsn(Opcodes.ALOAD, 0)
    mv.visitFieldInsn(Opcodes.GETFIELD, name, "callback", L(consumer))
    mv.visitVarInsn(Opcodes.ALOAD, 1)
    mv.visitMethodInsn(Opcodes.INVOKEINTERFACE, consumer, "accept", "(" + L(obj) + ")V", true)
    mv.visitInsn(Opcodes.RETURN)
    mv.visitMaxs(2, 2)
    mv.visitEnd()
  }
  cw.visitEnd()

  const inst = defineClassBytes(name, cw.toByteArray())
    .getDeclaredConstructor(Consumer.class)
    .newInstance(new java.util.function.Consumer({
      accept: function (t) { cb(t) } 
    }))
  LoadedInsts.push(inst)
  return inst;
}

const unregisterForge = inst => {
  MinecraftForge.EVENT_BUS.unregister(inst)
}

register("gameUnload", () => {
  LoadedInsts.forEach(unregisterForge) 
  LoadedInsts.length = 0
})

export { registerForge, unregisterForge }