mirror of
https://github.com/eaglerforge/EaglerForgeInjector
synced 2025-07-23 06:01:38 -09:00
ModAPI.promisify
This commit is contained in:
parent
a969f35b9a
commit
541ac83f5f
@ -94,6 +94,9 @@ The ModAPI object has the following methods:
|
||||
- Triggers a right click ingame.
|
||||
- `getFPS() : int`
|
||||
- Gets the frames per second of the game
|
||||
- `promisify(asyncJavaMethod: Method | Constructor) : PromisifiedJavaRunner`
|
||||
- Allows running java methods that are @Async/@Async dependent.
|
||||
- More [PromisifyDocumentation](promisify.md)
|
||||
|
||||
|
||||
## Handling strings, numbers and booleans to and from java.
|
||||
|
26
docs/apidoc/promisify.md
Normal file
26
docs/apidoc/promisify.md
Normal file
@ -0,0 +1,26 @@
|
||||
## ModAPI.promisify()
|
||||
Some methods in java are asynchronous, meaning that they don't return a value/modify state immediately. Calling them in an event or in a patch to a normal function will cause a stack implosion, characterised by the client/dedicated server hanging without any error messages.
|
||||
|
||||
In order to call them properly from javascript, you need to use the `ModAPI.promisify()` function.
|
||||
|
||||
For example, here we have a simple client-side command that will try to use the `PlatformRuntime` class to download data from a URI:
|
||||
```javascript
|
||||
ModAPI.addEventListener("sendchatmessage", function downloadSomething(e) {
|
||||
if (e.message.toLowerCase().startsWith("/downloadtest")) {
|
||||
var arraybuffer = ModAPI.hooks.methods.nlevi_PlatformRuntime_downloadRemoteURI(ModAPI.util.str("data:text/plain,hi"));
|
||||
console.log(arraybuffer);
|
||||
}
|
||||
});
|
||||
```
|
||||
This will cause the client to hang. The correct way of calling this asynchronous method is like this:
|
||||
```javascript
|
||||
ModAPI.addEventListener("sendchatmessage", function downloadSomething(e) {
|
||||
if (e.message.toLowerCase().startsWith("/downloadtest")) {
|
||||
ModAPI.promisify(ModAPI.hooks.methods.nlevi_PlatformRuntime_downloadRemoteURI)(ModAPI.util.str("data:text/plain,hi")).then(arraybuffer => {
|
||||
console.log(arraybuffer);
|
||||
});
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
You can replace the argument with any other method or constructor, including non asynchronous ones.
|
@ -19,6 +19,9 @@ Calling methods while the TeaVM thread is in a critical transition state (see `M
|
||||
Update 22/09/2024:
|
||||
See Asynchronous Code
|
||||
|
||||
Update 4/10/2024:
|
||||
@Async issue solved, see [PromisifyDocumentation](apidoc/promisify.md)
|
||||
|
||||
#### TeaVM thread suspension/resumption
|
||||
TeaVM allows for writing asynchronous callbacks, which eaglercraft uses for file operations and downloading from URIs. However, when a method that makes use of an async callback gets run from ModAPI, it triggers a stack implosion due to mismatches in value types upon return (as well as a whole other myriad of symptoms). Currently this is not supported by ModAPI, and it will take some time until it will be. In the meanwhile, avoid using constructors or methods that access a file or use other asynchronous apis. Examples:
|
||||
- Constructing an EntityPlayerMP
|
||||
|
@ -22,25 +22,21 @@
|
||||
|
||||
// Get the EntityPlayerMP class to spawn the fake player
|
||||
const EntityPlayerMPClass = ModAPI.reflect.getClassById("net.minecraft.entity.player.EntityPlayerMP");
|
||||
setTimeout(() => {
|
||||
ModAPI.hooks._teavm.$rt_startThread(() => {
|
||||
return EntityPlayerMPClass.constructors[0](ModAPI.server.getRef(), world.getRef(), fakeProfile, playerInteractionManager);
|
||||
}, function (...args) {
|
||||
console.log(this, args);
|
||||
var fakePlayer = ModAPI.util.wrap(args[0]);
|
||||
ModAPI.promisify(EntityPlayerMPClass.constructors[0])(ModAPI.server.getRef(), world.getRef(), fakeProfile, playerInteractionManager).then(result => {
|
||||
console.log(result);
|
||||
var fakePlayer = ModAPI.util.wrap(result);
|
||||
|
||||
// Set the fake player position to be near the command sender
|
||||
console.log(senderPos);
|
||||
fakePlayer.setPosition(senderPos.getX(), senderPos.getY(), senderPos.getZ());
|
||||
// Set the fake player position to be near the command sender
|
||||
console.log(senderPos);
|
||||
fakePlayer.setPosition(senderPos.getX(), senderPos.getY(), senderPos.getZ());
|
||||
|
||||
// Add the fake player to the world
|
||||
world.spawnEntityInWorld(fakePlayer.getRef());
|
||||
// Add the fake player to the world
|
||||
world.spawnEntityInWorld(fakePlayer.getRef());
|
||||
|
||||
// Notify the player that the fake player has been spawned
|
||||
const ChatComponentTextClass = ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText");
|
||||
event.sender.addChatMessage(ChatComponentTextClass.constructors[0](ModAPI.util.str("Fake Steve has been spawned!")));
|
||||
});
|
||||
}, 1);
|
||||
// Notify the player that the fake player has been spawned
|
||||
const ChatComponentTextClass = ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText");
|
||||
event.sender.addChatMessage(ChatComponentTextClass.constructors[0](ModAPI.util.str("Fake Steve has been spawned!")));
|
||||
});
|
||||
|
||||
|
||||
// Prevent the command from executing further
|
||||
|
@ -3,6 +3,11 @@ ModAPI.hooks._teavm.$rt_startThread(() => {
|
||||
return ModAPI.hooks.methods.nlevi_PlatformRuntime_downloadRemoteURI(ModAPI.util.str("data:text/plain,hi"))
|
||||
}, function (...args) { console.log(this, args) })
|
||||
|
||||
// SUCCESS - Runs anywhere, anytime. Might work with async/await, but for now stick to .then()
|
||||
ModAPI.promisify(ModAPI.hooks.methods.nlevi_PlatformRuntime_downloadRemoteURI)(ModAPI.util.str("data:text/plain,hi")).then(result => {
|
||||
console.log(result); //Log arraybuffer
|
||||
});
|
||||
|
||||
|
||||
//WIP - Pausing and resuming client thread
|
||||
globalThis.suspendTest = function (...args) {
|
||||
|
19
postinit.js
19
postinit.js
@ -543,6 +543,23 @@ globalThis.modapi_postinit = "(" + (() => {
|
||||
ModAPI.hooks.freezeCallstack = false;
|
||||
}
|
||||
|
||||
//Function used for running @Async / @Async-dependent TeaVM methods.
|
||||
ModAPI.promisify = function promisify(fn) {
|
||||
return function promisifiedJavaMethpd(...inArguments) {
|
||||
return new Promise((res, rej) => {
|
||||
Promise.resolve().then( //queue microtask
|
||||
() => {
|
||||
ModAPI.hooks._teavm.$rt_startThread(() => {
|
||||
return fn(...inArguments);
|
||||
}, function (out) {
|
||||
res(out);
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
ModAPI.util.string = ModAPI.util.str = ModAPI.hooks._teavm.$rt_str;
|
||||
|
||||
ModAPI.util.setStringContent = function (jclString, string) {
|
||||
@ -671,7 +688,7 @@ globalThis.modapi_postinit = "(" + (() => {
|
||||
const desktopServerStartupMethod = ModAPI.hooks.methods[desktopServerStartup];
|
||||
ModAPI.hooks.methods[desktopServerStartup] = function (...args) {
|
||||
var x = desktopServerStartupMethod.apply(this, args);
|
||||
ModAPI.dedicatedServer._data.forEach((code)=>{
|
||||
ModAPI.dedicatedServer._data.forEach((code) => {
|
||||
(new Function(code))();
|
||||
});
|
||||
console.log("[ModAPI] Hooked into external integrated server.");
|
||||
|
Loading…
x
Reference in New Issue
Block a user