Skip to content

CoreMods 5.2: ES6 language support, additional ASMAPI methods, and all-around cleanup #64

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,23 @@
# CoreMods

New JavaScript based system for implementing CoreMods.
CoreMods is a JavaScript-based system that acts as a wrapper around ObjectWeb ASM.

Why?
## Purpose

Because it means that it's a lot easier to manage the lifecycle correctly. We can isolate
CoreMod logic to the proper ClassLoading contexts without effort on the part of the Modder.
CoreMods need to be sandboxed, or otherwise isolated, in their own environments so that they are not able to cause early
class-loading. They transform classes as only as they are loaded and do not have access to objects outside of the
sandbox given to them. This helps prevent issues that would otherwise arise from CoreMods written traditionally in Java.

It hopefully also communicates that CoreMods are strictly arms-length : they operate on
classes as they load _only_ - changing structures and behaviours through that means.
Since CoreMods integrates with ModLauncher's transformation system, it is easier to manage the lifecycle as CoreMods is
only responsible for managing the transformation as ModLauncher is instead the one responsible for providing the class
loading system.

This is connected to Forge and FML through the CoreMod SPI being implemented in new Forge.
## Usage

CoreMods are JavaScript files that are sandboxed by the limitations provided within the CoreMod engine. It is only able
to access a limited set of classes and packages. ASMAPI, included within CoreMods, exists to provide several helpful
tools for writing CoreMods. You can view this class yourself to see its usages, or you can find examples of it in other
CoreMods.

The best way to find examples for CoreMods is to look at Forge itself, since it includes complex examples that utilize
much of the functionality within the sandbox.
6 changes: 5 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,15 @@ dependencies {
testImplementation('org.powermock:powermock-core:2.0.+')
testImplementation('org.hamcrest:hamcrest-core:2.2')
testImplementation('org.apache.logging.log4j:log4j-core:2.19.0')
testImplementation(libs.modlauncher)
testImplementation(libs.forgespi)
testImplementation(libs.unsafe)
testCompileOnly('org.jetbrains:annotations:21.0.1')
testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.7.+')
testRuntimeOnly(project(':coremods-test-jar'))

compileOnly(libs.modlauncher)
compileOnly(libs.securemodules)
implementation(libs.log4j.api)
api(libs.bundles.asm)
compileOnly(libs.forgespi)
Expand Down
3 changes: 3 additions & 0 deletions coremods-test/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@ dependencies {
testImplementation(project(':coremods-test-jar'))
testImplementation(libs.junit.api)
testImplementation(libs.log4j.api)
testImplementation(libs.modlauncher)
testImplementation(libs.forgespi)
testImplementation(libs.unsafe)
testRuntimeOnly(libs.bundles.junit.runtime)
testCompileOnly(libs.nulls)
}
Expand Down
17 changes: 11 additions & 6 deletions coremods-test/src/test/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,15 @@
* SPDX-License-Identifier: LGPL-2.1-only
*/
module net.minecraftforge.coremods.test {
requires cpw.mods.modlauncher;
requires net.minecraftforge.forgespi;
requires org.junit.jupiter.api;
requires org.apache.logging.log4j;
requires cpw.mods.modlauncher;
requires org.objectweb.asm.tree;
requires org.jetbrains.annotations;
}
requires net.minecraftforge.coremod;
requires org.junit.jupiter.api;
requires org.apache.logging.log4j;
requires org.objectweb.asm.tree;
requires org.jetbrains.annotations;
requires net.minecraftforge.unsafe;

provides cpw.mods.modlauncher.api.ITransformationService
with net.minecraftforge.coremod.test.TestTransformerService;
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,12 @@

import net.minecraftforge.coremod.CoreModEngine;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.stream.Collectors;

import net.minecraftforge.forgespi.coremod.ICoreModFile;
import net.minecraftforge.unsafe.UnsafeHacks;
import org.junit.jupiter.api.Test;
import org.objectweb.asm.tree.ClassNode;

Expand All @@ -18,11 +21,13 @@ public class CoreModTest {

@SuppressWarnings("unchecked")
@Test
void testJSLoading() {
void testJSLoading() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
final CoreModEngine coreModEngine = new CoreModEngine();
coreModEngine.loadCoreMod(new JSFileLoader("src/test/javascript/testcoremod.js"));
coreModEngine.loadCoreMod(new JSFileLoader("src/test/javascript/testcore2mod.js"));
coreModEngine.loadCoreMod(new JSFileLoader("src/test/javascript/testdata.js"));
var loadCoreMod = coreModEngine.getClass().getMethod("loadCoreMod", ICoreModFile.class);
UnsafeHacks.setAccessible(loadCoreMod);
loadCoreMod.invoke(coreModEngine, new JSFileLoader("src/test/javascript/testcoremod.js"));
loadCoreMod.invoke(coreModEngine, new JSFileLoader("src/test/javascript/testcore2mod.js"));
loadCoreMod.invoke(coreModEngine, new JSFileLoader("src/test/javascript/testdata.js"));
final List<ITransformer<?>> iTransformers = coreModEngine.initializeCoreMods();
iTransformers.forEach(t -> {
System.out.printf("targ: %s\n", t.targets().stream().map(ITransformer.Target::getClassName).collect(Collectors.joining(",")));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,11 @@
import cpw.mods.modlauncher.api.ITransformer;
import net.minecraftforge.coremod.CoreModEngine;

import net.minecraftforge.forgespi.coremod.ICoreModFile;
import net.minecraftforge.unsafe.UnsafeHacks;
import org.junit.jupiter.api.Test;

import java.lang.reflect.InvocationTargetException;
import java.util.List;
import java.util.concurrent.Callable;

Expand All @@ -21,14 +24,16 @@ public static List<ITransformer<?>> getTransformers() {
}

@Test
public void testCoreModLoading() {
public void testCoreModLoading() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
System.setProperty("test.harness", "out/production/classes,out/test/classes,out/testJars/classes,build/classes/java/main,build/classes/java/test,build/classes/java/testJars");
System.setProperty("test.harness.callable", "net.minecraftforge.coremod.test.TestLaunchTransformer$Callback");

cme.loadCoreMod(new JSFileLoader("src/test/javascript/testcoremod.js"));
cme.loadCoreMod(new JSFileLoader("src/test/javascript/testcore2mod.js"));
cme.loadCoreMod(new JSFileLoader("src/test/javascript/testmethodcoremod.js"));
cme.loadCoreMod(new JSFileLoader("src/test/javascript/testmethodcoreinsert.js"));
var loadCoreMod = cme.getClass().getMethod("loadCoreMod", ICoreModFile.class);
UnsafeHacks.setAccessible(loadCoreMod);
loadCoreMod.invoke(cme, new JSFileLoader("src/test/javascript/testcoremod.js"));
loadCoreMod.invoke(cme, new JSFileLoader("src/test/javascript/testcore2mod.js"));
loadCoreMod.invoke(cme, new JSFileLoader("src/test/javascript/testmethodcoremod.js"));
loadCoreMod.invoke(cme, new JSFileLoader("src/test/javascript/testmethodcoreinsert.js"));

Launcher.main("--version", "1.0", "--launchTarget", "testharness");
}
Expand Down
4 changes: 4 additions & 0 deletions coremods-test/src/test/javascript/testcore2mod.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
Comment on lines +1 to +4
Copy link
Contributor

@PaintNinja PaintNinja Nov 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would personally prefer if these licence headers were skipped for JS files, but leaving Lex to decide on that. Note that Nashorn has a fast-path for files under 512 bytes, so adding licence headers may slow down load times.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly I have no opinion on this, I don't think it matters tho especially for test scripts as they should be small and are only used for tests not production.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about the CoreMods in Forge itself?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All of them are already over 512B so its a non-issue having the headers in there.

var core2fn = function() {
print("Core2 function!");
}
Expand Down
4 changes: 4 additions & 0 deletions coremods-test/src/test/javascript/testcoremod.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
function initializeCoreMod() {
print("Hello");
Java.type('net.minecraftforge.coremod.api.ASMAPI').loadFile('testcoremod2.js')
Expand Down
4 changes: 4 additions & 0 deletions coremods-test/src/test/javascript/testcoremod2.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
function moreFunctions() {
print("Poopy from more functions!");
}
4 changes: 4 additions & 0 deletions coremods-test/src/test/javascript/testdata.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
function initializeCoreMod() {
var data = Java.type('net.minecraftforge.coremod.api.ASMAPI').loadData('testdata.json')
print('Loaded JSON: ' + JSON.stringify(data))
Expand Down
6 changes: 5 additions & 1 deletion coremods-test/src/test/javascript/testmethodcoreinsert.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
function initializeCoreMod() {
return {
'coremodmethod': {
'target': {
'type': 'METHOD',
'class': 'cpw.mods.TestClass',
'class': 'net.minecraftforge.coremods.testjar.TestClass',
'methodName': 'testInsert',
'methodDesc': '()Z'
},
Expand Down
6 changes: 5 additions & 1 deletion coremods-test/src/test/javascript/testmethodcoremod.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
function initializeCoreMod() {
return {
'coremodmethod': {
'target': {
'type': 'METHOD',
'class': 'cpw.mods.TestClass',
'class': 'net.minecraftforge.coremods.testjar.TestClass',
'methodName': 'testMethod',
'methodDesc': '()Z'
},
Expand Down

This file was deleted.

6 changes: 3 additions & 3 deletions settings.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -24,17 +24,17 @@ dependencyResolutionManagement {
library('junit-platform-launcher', 'org.junit.platform:junit-platform-launcher:1.10.0')
bundle('junit-runtime', ['junit-engine', 'junit-platform-launcher'])

library('unsafe', 'net.minecraftforge:unsafe:0.9.2')
library('securemodules', 'net.minecraftforge:securemodules:2.2.20') // Needs unsafe
/*
library('unsafe', 'net.minecraftforge:unsafe:0.9.1')
library('securemodules', 'net.minecraftforge:securemodules:2.2.0') // Needs unsafe
library('gson', 'com.google.code.gson:gson:2.10.1')
library('jopt-simple', 'net.sf.jopt-simple:jopt-simple:5.0.4')
*/

library('forgespi', 'net.minecraftforge:forgespi:7.1.0')
library('modlauncher', 'net.minecraftforge:modlauncher:10.1.1') // Needs securemodules
library('nulls', 'org.jetbrains:annotations:23.0.0')
library('nashorn', 'org.openjdk.nashorn:nashorn-core:15.3') // Needed by coremods, because the JRE no longer ships JS
library('nashorn', 'org.openjdk.nashorn:nashorn-core:15.4') // Needed by coremods, because the JRE no longer ships JS

version('log4j', '2.19.0')
library('log4j-api', 'org.apache.logging.log4j', 'log4j-api' ).versionRef('log4j')
Expand Down
20 changes: 20 additions & 0 deletions src/main/java/module-info.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) Forge Development LLC
* SPDX-License-Identifier: LGPL-2.1-only
*/
module net.minecraftforge.coremod {
// CoreMods framework
exports net.minecraftforge.coremod;
// ASMAPI
exports net.minecraftforge.coremod.api;

requires cpw.mods.modlauncher;
requires net.minecraftforge.forgespi;
requires org.apache.logging.log4j;
requires org.jetbrains.annotations;
requires org.openjdk.nashorn;
requires org.objectweb.asm.util;

provides net.minecraftforge.forgespi.coremod.ICoreModProvider
with net.minecraftforge.coremod.CoreModProvider;
}
Loading