From 9555af5b1acdad7dd2a2f1fce19a6ec2c52a6361 Mon Sep 17 00:00:00 2001 From: ZXMushroom63 Date: Sat, 25 Jan 2025 17:26:41 +0800 Subject: [PATCH] progress on custom entities --- docs/apidoc/keygen.md | 6 ++- docs/apidoc/reflect.md | 5 ++- examplemods/cubeentity.js | 87 ++++++++++++++++++++++++++++++++++++--- postinit.js | 4 +- 4 files changed, 91 insertions(+), 11 deletions(-) diff --git a/docs/apidoc/keygen.md b/docs/apidoc/keygen.md index 17c5cd9..1e3a401 100644 --- a/docs/apidoc/keygen.md +++ b/docs/apidoc/keygen.md @@ -1,8 +1,10 @@ ## ModAPI.keygen -ModAPI.keygen contains the API for getting item and block IDs from a string. It looks at the registries for items and blocks to derive the IDs, so IDs will not be automatically reserved until a block/item is actually registered. Ideally, you'd want to call a keygen method just before registering your block. +ModAPI.keygen contains the API for getting numerical item, block and entity IDs from a string. It looks at registries to derive the IDs, so IDs will not be automatically reserved until a block/item is actually registered into the game. Ideally, you'd want to call a keygen method just before registering your block. Methods: - `ModAPI.keygen.item(itemId: String) : number` - Example usage is: `var id = ModAPI.keygen.item("my_example_item");` - `ModAPI.keygen.block(blockId: String) : number` - - Example usage is: `var id = ModAPI.keygen.block("my_example_block");` \ No newline at end of file + - Example usage is: `var id = ModAPI.keygen.block("my_example_block");` +- `ModAPI.keygen.entity(entityId: String) : number` + - Example usage is: `var id = ModAPI.keygen.entity("my_example_entity");` \ No newline at end of file diff --git a/docs/apidoc/reflect.md b/docs/apidoc/reflect.md index b8bdd88..0aff6cc 100644 --- a/docs/apidoc/reflect.md +++ b/docs/apidoc/reflect.md @@ -16,9 +16,10 @@ Methods: - This method is used to find a class by its id. - For example, to get the `Minecraft` class, you can use `ModAPI.reflect.getClassById("Minecraft")` - This runs slower than `getClassById` because it has to filter through all classes. Make sure to cache the result rather than calling it over and over again. -- `ModAPI.reflect.getSuper(rClass: ReflectClass, filter: Function) : Function` +- `ModAPI.reflect.getSuper(rClass: ReflectClass, filter: Function?) : Function` - Gets a super function from a reflect class. This is used to extend built in classes, like `Block`. - - For an example, see lines [29](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L29) and [33](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L33) in unlucky_blocks.js + - For an example, see lines [29](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L29) and [33](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L33) in `unlucky_blocks.js` + - When called without a filter function, the filter defaults to `(fn)=>fn.length === 1` - `ModAPI.reflect.prototypeStack(rClass: ReflectClass, target: Class/ConstructorFunction) : void` - Copies methods from a reflect class and it's parents onto a target native JavaScript class. This allows TeaVM to use these objects normally, without you having to manually reimplement every method. In other words, this is the equivalent of extending a class. - [Example usage](https://github.com/eaglerforge/EaglerForgeInjector/blob/6e8598c180f96a65c0c101be72e6d0fa53195404/examplemods/unlucky_blocks.js#L37) diff --git a/examplemods/cubeentity.js b/examplemods/cubeentity.js index 8594633..1dba60b 100644 --- a/examplemods/cubeentity.js +++ b/examplemods/cubeentity.js @@ -5,12 +5,87 @@ ModAPI.meta.credits("By ZXMushroom64"); function registerEntity() { - + // Utils + const ResourceLocation = ModAPI.reflect.getClassByName("ResourceLocation").constructors.find(x=>x.length===1); + const GlStateManager = Object.fromEntries(Object.values(ModAPI.reflect.getClassByName("GlStateManager").staticMethods).map(x=>[x.methodNameShort, x.method])); + + // START CUSTOM ENTITY + var entityClass = ModAPI.reflect.getClassById("net.minecraft.entity.Entity"); + var entitySuper = ModAPI.reflect.getSuper(entityClass, (x) => x.length === 2); + var nme_EntityCube = function nme_EntityCube($worldIn) { + entitySuper(this, $worldIn); + this.$setCreativeTab(creativeMiscTab); + this.$preventEntitySpawning = 1; + this.$setSize(1, 1); + } + ModAPI.reflect.prototypeStack(entityClass, nme_EntityCube); + nme_EntityCube.prototype.$canTriggerWalking = function () { return 0 }; + nme_EntityCube.prototype.$canBePushed = function () { return 0 }; + nme_EntityCube.prototype.$getCollisionBox = function () { return this.$getEntityBoundingBox() }; + nme_EntityCube.prototype.$getCollisionBoundingBox = function () { return this.$getEntityBoundingBox() }; + // END CUSTOM ENTITY + + + // START CUSTOM MODEL + var ModelRenderer = ModAPI.reflect.getClassById("net.minecraft.client.model.ModelRenderer").constructors.find(x => x.length === 1); + var modelBaseClass = ModAPI.reflect.getClassById("net.minecraft.client.model.ModelBase"); + var modelBaseSuper = ModAPI.reflect.getSuper(entityClass); //while super isn't used when extending this class, java implies the call. + var nmcm_ModelCube = function nmcm_ModelCube() { + modelBaseSuper(this); + this.$textureWidth = 64; + this.$textureHeight = 64; + this.$cubeRenderer = ModelRenderer(this).$setTextureOffset(0, 0); + this.$cubeRenderer.$addBox0(0, 0, 0, 1, 1, 1); + this.$cubeRenderer.$setRotationPoint(0, 0, 0); + } + ModAPI.reflect.prototypeStack(modelBaseClass, nmcm_ModelCube); + nmcm_ModelCube.prototype.$render = function ($entity, useless1, useless2, partialTicks, useless3, useless4, f) { + this.$cubeRenderer.$render(f); + } + // END CUSTOM MODEL + + + // START CUSTOM RENDERER + var renderClass = ModAPI.reflect.getClassById("net.minecraft.client.renderer.entity.Render"); + var renderSuper = ModAPI.reflect.getSuper(entityClass, (x)=>x.length === 2); + const cubeTextures = ResourceLocation(ModAPI.util.str("textures/entity/cube.png")); + var nmcre_RenderCube = function nmcre_RenderCube(renderManager) { + renderSuper(this, renderManager); + this.$modelCube = new nmcm_ModelCube(); + this.$shadowSize = 0.5; + } + ModAPI.reflect.prototypeStack(renderClass, nmcre_RenderCube); + nmcre_RenderCube.prototype.$getEntityTexture = function (entity) { + return cubeTextures; + } + const parentDoRender = nmcre_RenderCube.prototype.$doRender; + nmcre_RenderCube.prototype.$doRender = function (entity, x, y, z, yaw, pitch) { + GlStateManager.pushMatrix(); + GlStateManager.translate(x, y + 0.25, z); + GlStateManager.rotate(180 - yaw, 0, 1, 0); + this.$bindEntityTexture(entity); + this.$modelCube.$render(entity, 0, 0, -0.1, 0, 0, 0.0625); + GlStateManager.popMatrix(); + parentDoRender.apply(this, [entity, x, y, z, yaw, pitch]); + } + + ModAPI.reflect.getClassById("net.minecraft.entity.EntityList").staticMethods.addMapping0( + ModAPI.util.asClass(nme_EntityCube), + { + $createEntity: nme_EntityCube + }, + ModAPI.util.str("Cube"), + ModAPI.keygen.entity("cube"), + 0x000000, //egg base + 0x00FF00 //egg spots + ); + ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(nme_EntityCube), new nmcre_RenderCube(ModAPI.mc.renderManager.getRef())); return { - EntityCube: null, - ModelCube: null, - RenderCube: null + EntityCube: nme_EntityCube, + ModelCube: nmcm_ModelCube, + RenderCube: nmcre_RenderCube, + cubeTexture: cubeTextures } } @@ -18,7 +93,9 @@ var data = registerEntity(); ModAPI.addEventListener("lib:asyncsink", async () => { - //ModAPI.mc.renderManager.entityRenderMap.put(ModAPI.util.asClass(wellGetThere), new data.RenderCube(ModAPI.mc.renderManager.getRef())) AsyncSink.L10N.set("entity.Cube.name", "Cube (TM)"); + AsyncSink.setFile("resourcepacks/AsyncSinkLib/assets/minecraft/textures/entity/cube.png", await (await fetch( + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAAAXNSR0IArs4c6QAAAQBJREFUeF7l0BFzAmAAgOGvKxgMgiAYDIJgEARBEASDQTAIgiAYBEEQBN0NBkEQBEEQBIMgCAZBEAwGgyAIgiAIgiConxE88PJ790RCCNdYCOGeRe/4j4SYDvCgAzzqAHEdIKEDJHWAJx3gWQdI6QBpHeBFB8joAFkdIKcD5HWAgg5Q1AFedYA3HaCkA7zrAGUdoKIDVHWAmg7woQPUdYCGDtDUAVo6QFsH6OgAnzrAlw7Q1QF6OkBfBxjoAEMdYKQDjHWAiQ7wrQNMdYCZDjDXAX50gIUOsNQBVjrArw7wpwP86wBrHWCjA2x1gJ0OsNcBDjrAUQc46QBnHeBiA9wALSueIjTE4PwAAAAASUVORK5CYII=" + )).arrayBuffer()); }); })(); \ No newline at end of file diff --git a/postinit.js b/postinit.js index f745446..9c35081 100644 --- a/postinit.js +++ b/postinit.js @@ -166,7 +166,7 @@ globalThis.modapi_postinit = "(" + (() => { return name; } - ModAPI.util.asClass = ModAPI.util.asClass = ModAPI.hooks._teavm.$rt_cls; + ModAPI.util.asClass = ModAPI.hooks._teavm.$rt_cls; ModAPI.util.wrap = function (outputValue, target, corrective, disableFunctions) { target ||= {}; @@ -413,7 +413,7 @@ globalThis.modapi_postinit = "(" + (() => { //Magical function for making a subclass with a custom constructor that you can easily use super(...) on. ModAPI.reflect.getSuper = function getSuper(reflectClass, filter) { - filter ||= () => true; + filter ||= (x) => x.length === 1; var initialiser = reflectClass.internalConstructors.find(filter); return function superFunction(thisArg, ...extra_args) { reflectClass.class.call(thisArg);