Fix more stack implosion occurences

This commit is contained in:
ZXMushroom63 2024-09-13 18:50:13 +08:00
parent 03f62846cc
commit 195a50315c
6 changed files with 53 additions and 14 deletions

View File

@ -4,7 +4,7 @@ The EaglerForge ModAPI is housed in a global JavaScript object stored on `global
The global object has the following properties:
- `ModAPI.player: EntityPlayerSP`
- Only accessible after `ModAPI.require("player")` is called, this is the local player entity. It is regenerated every time the `update` event is called.
- `ModAPI.world: WorldClient`
- `ModAPI.world: WorldClient`
- Only accessible after `ModAPI.require("world")` is called, this is the client-side world. It is regenerated every time the `update` event is called.
- `ModAPI.network: NetHandlerPlayClient`
- Only accessible after `ModAPI.require("network")` is called, this is the client's networking handler. It is regenerated every time the `update` event is called.

View File

@ -24,11 +24,16 @@ Methods:
- Alias: `ModAPI.util.ustr()`
- Alias: `ModAPI.util.unstring()`
- Alias: `ModAPI.util.jclStrToJsStr()`
- `ModAPI.util.getMethodFromPackage(classId: String, methodName: String)`
- `ModAPI.util.getMethodFromPackage(classId: String, methodName: String) : String`
- Takes a class id (eg: `net.minecraft.client.Minecraft`) and a method name (eg: `middleClickMouse`) and returns its key in `ModAPI.hooks.methods`.
- `ModAPI.util.stringToUint16Array(string: String) : Uint16Array`
- Encodes a string into a uint16array.
- `ModAPI.util.setStringContent(jclString: java.lang.String, contents: String) : void`
- Writes a new javascript string into the contents of a java string.
- `ModAPI.util.getMethodFromPackage(classId: String, methodName: String)`
- Takes a class id (eg: `net.minecraft.client.Minecraft`) and a method name (eg: `middleClickMouse`) and returns its key in `ModAPI.hooks.methods`.
- `ModAPI.util.getMethodFromPackage(classId: String, methodName: String) : String`
- Takes a class id (eg: `net.minecraft.client.Minecraft`) and a method name (eg: `middleClickMouse`) and returns its key in `ModAPI.hooks.methods`.
- `ModAPI.util.hashCode(string: String) : String`
- Returns the hash of a string.
- `ModAPI.util.isCritical() : boolean`
- Checks wether the thread is in a critical state.
- When patching methods, it is good practice to allow the method to resume as usual if this is `true`, to avoid stack implosions. (yes, those are real)

View File

@ -11,4 +11,7 @@ When I was trying to hook into the server-side processing of chat messages, I fo
Incorrectly patching methods with ModAPI.hooks (such as returning `false` instead of `0`, or a javascript string without using `ModAPI.str()`) will effectively cause the memory stack to implode, along with all crash handlers. I came across this when I was using the `processcommand` event with preventDefault set to true. I didn't return any value when patching methods for the event being `preventDefault`ed, when the output expected was a java boolean (`0`/`1`). This would cause the dedicated server to freeze/lock up, without triggering any form of crash.
Update 13/09/2024:
Any form of incorrect data type, even passing the wrong values, can cause this sort of hang. I encountered this when trying to set a block in the world to any form of wood or leaf block, without adding iproperties to the tree type.
Any form of incorrect data type, even passing the wrong values, can cause this sort of hang. I encountered this when trying to set a block in the world to any form of wood or leaf block, without adding iproperties to the tree type.
Update 13/09/2024:
Calling methods while the TeaVM thread is in a critical transition state (see `ModAPI.util.isCritical()`) will shift the call stack, cause methods to access the incorrect values at runtime, and also cause the stack to implode. Gotta love TeaVM.

View File

@ -8,8 +8,16 @@ ModAPI.dedicatedServer.appendCode(function () {
var rayTraceMethod = worldMethodMap[Object.keys(worldMethodMap).filter(key => {
return key.startsWith("rayTraceBlocks") && worldMethodMap[key].method.length === 4;
})].method;
var blockPosConstructor = ModAPI.reflect.getClassById("net.minecraft.util.BlockPos").constructors.find((x) => { return x.length === 3 });
var blockTypesList = Object.keys(ModAPI.blocks);
var blockTypesList = [];
ModAPI.addEventListener("serverstart", () => {
blockTypesList = Object.keys(ModAPI.blocks).filter(key => {
var blockType = ModAPI.blocks[key];
if (!blockType) {
return false;
}
return blockType.fullBlock && !blockType.needsRandomTick;
});
});
function getPlayerEntitiesAndTheirWorld() {
var out = [];
ModAPI.server.worldServers.forEach(x => {
@ -31,11 +39,12 @@ ModAPI.dedicatedServer.appendCode(function () {
ModAPI.addEventListener("processcommand", (event) => {
if (event.command.toLowerCase().startsWith("/blocklook")) {
active = !active;
console.log(blockTypesList);
var playerEntities = getPlayerEntitiesAndTheirWorld();
playerEntities.forEach(pair => {
pair.player.addChatMessage(
ModAPI.reflect.getClassById("net.minecraft.util.ChatComponentText").constructors[0](ModAPI.util.str(
"[BlockLook] Toggled to " + (active ? "on" : off)
"[BlockLook] Toggled to " + (active ? "on" : "off")
))
)
});
@ -45,7 +54,7 @@ ModAPI.dedicatedServer.appendCode(function () {
var t = 0;
ModAPI.addEventListener("tick", () => {
t++;
if (t > 20) {
if (t > 5) {
t = 0;
} else {
return;
@ -53,6 +62,9 @@ ModAPI.dedicatedServer.appendCode(function () {
if (!active) {
return;
}
if (blockTypesList.length < 1) {
return;
}
var playerEntities = getPlayerEntitiesAndTheirWorld();
playerEntities.forEach(pair => {
var start = pair.player.getPositionEyes(1).getRef();
@ -63,21 +75,22 @@ ModAPI.dedicatedServer.appendCode(function () {
lookVector.addVector(start.$xCoord, start.$yCoord, start.$zCoord);
var hitResult = rayTraceMethod(pair.world.getRef(), start, lookVector.getRef(), 0);
if (hitResult) {
console.log(hitResult);
if (ModAPI.util.unstr(hitResult.$typeOfHit.$name5) !== "BLOCK") {
return console.log("Non block collision detected.")
}
var blockPos = hitResult.$blockPos;
if (!pair.world.isBlockLoaded(blockPos)) {
console.log("[BlockLook] Block is not loaded!");
return console.log("[BlockLook] Block is not loaded!");
}
var blockType = blockTypesList[Math.floor(Math.random() * blockTypesList.length)];
blockType = ModAPI.blocks[blockType];
if (!blockType.fullBlock) {
if (!blockType.fullBlock || blockType.needsRandomTick) {
return;
}
console.log("[BlockLook] " + ModAPI.util.unstr(blockType.unlocalizedName.getRef()));
var block = blockType.getDefaultState();
pair.world.setBlockState(blockPos, block.getRef(), 2);
pair.world.notifyNeighborsRespectDebug(blockPos, block.getRef());
}
});
});
})
});

View File

@ -509,6 +509,12 @@ globalThis.modapi_postinit = `(() => {
return Math.floor(Math.abs(hash)) + "";
};
//Check whether the thread is in a critical transition state (resuming or suspending)
//Calling functions in a critical state will cause stack implosions.
ModAPI.util.isCritical = function isCritical() {
return ModAPI.hooks._teavm.$rt_suspending() || ModAPI.hooks._teavm.$rt_resuming();
}
ModAPI.clickMouse = function () {
ModAPI.hooks.methods["nmc_Minecraft_clickMouse"](ModAPI.javaClient);
}
@ -573,6 +579,9 @@ globalThis.modapi_postinit = `(() => {
const serverTickMethodName = ModAPI.util.getMethodFromPackage("net.minecraft.server.MinecraftServer", "tick");
const serverTickMethod = ModAPI.hooks.methods[serverTickMethodName];
ModAPI.hooks.methods[serverTickMethodName] = function ($this) {
if (ModAPI.util.isCritical()) {
return serverTickMethod.apply(this, [$this]);
}
var data = { preventDefault: false }
ModAPI.events.callEvent("tick", data);
if (data.preventDefault) {

View File

@ -509,6 +509,12 @@
return Math.floor(Math.abs(hash)) + "";
};
//Check whether the thread is in a critical transition state (resuming or suspending)
//Calling functions in a critical state will cause stack implosions.
ModAPI.util.isCritical = function isCritical() {
return ModAPI.hooks._teavm.$rt_suspending() || ModAPI.hooks._teavm.$rt_resuming();
}
ModAPI.clickMouse = function () {
ModAPI.hooks.methods["nmc_Minecraft_clickMouse"](ModAPI.javaClient);
}
@ -573,6 +579,9 @@
const serverTickMethodName = ModAPI.util.getMethodFromPackage("net.minecraft.server.MinecraftServer", "tick");
const serverTickMethod = ModAPI.hooks.methods[serverTickMethodName];
ModAPI.hooks.methods[serverTickMethodName] = function ($this) {
if (ModAPI.util.isCritical()) {
return serverTickMethod.apply(this, [$this]);
}
var data = { preventDefault: false }
ModAPI.events.callEvent("tick", data);
if (data.preventDefault) {