Blog of a lonely developer

  • The FTB Forum is now read-only, and is here as an archive. To participate in our community discussions, please join our Discord! https://ftb.team/discord

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
I've seen some people expressing interest in the process behind mod development. Having started proper mod making in 2013, I can tell some things about that. So here goes something.
==============================================================================
Currently trying to make a mob that can go through blocks. Initial success was achieved after several hours, after figuring out how collision works in the game. Had to make custom path processor, path finder and navigator, as well as rewrite collision code of this particular mob. Now there remains a bug - this mob navigates only horizontally, which means that if he starts path at a block above ground and then descends, he tries to reach the end point which remains set in the air, thus he repeatedly jumps back and forth trying to reach the point. I guess I need to adjust it so the endpoint creates lower when this mob descends.
Another issue I can see is ascending. As it is now, the mob won't ascend, because he can just run through blocks. What if he has to attack a target that stands higher? The pathfinder must be adjusted so he can ascend blocks without going inside them, and collision code must be in sync with this. Quite a complicated task, eh?
*******************************************************************************************************************
Another thing I'm trying to make is an experience storage block. The vanilla code "shines" here. We have three variables related to exp: an integer named "experienceLevel" which stores amount of levels, a float "experience" which stores partial level (the one reflected by green bar in the HUD), and an integer "experienceTotal", which stores total experience amount, including partial level. Like, what the ...? There are 2 functions in player class which deal with exp - one that adds an arbitrary amount of exp, and one which adds an arbitrary amount of levels. The first function is overriden in EntityMP subclass (server-side player), where it sets some suspicious variable "lastExperience" to -1. Digging through code, it appeared that this variable is used to synchronize exp values to client-player. Not going too much into details, I've made a block which can deposit/credit all exp. at once, because that shiz is too complicated for me to handle. There remains an issue with synchronization, e.g. after exp. was deposited, the player still has it, hidden. No wonder there aren't many things that deal with experience, as its system was written so wonky. I'm thinking of just stealing exp conversion formulas from open-source mods, there is no point in reinventing the wheel and dealing with crap-code.
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
I looked at ActualAdditons code, where its Experience Solidifier is coded. There is a comment that says its experience conversion functions were excerpted from EnderIO. Interesting, because I already knew that those EnderIO's functions were taken from OpenBlocks. So I went and looked at OpenBlocks code, found those functions and adapted them for my experience storage. Now it works properly, and there are at least 3 mods now that took expereince calculation functions from OpenBlocks. Credit goes to its makers; this is open source power in action! ;)
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
Alexiy, peeps might appreciate links to or snippets of your code. There's a lot of lonely noob developers here who could learn your mistakes with you :)
Ok, some snippets wouldn't hurt me, I guess. If I come up with a nifty feature or encounter some interesting problems, I'll be sure to show it.
Today, I'm willing to share a piece of code for beginners which might save them some trouble when working with Containers. Yes, those classes that are responsible for handling UIs with item slots - if you are making a UI with item slots, you will be making a corresponding Container subclass as well. We know that many UIs have a convenient Shift+clicking feature, which puts items into right slots automatically. This behavior is defined in Container#transferStackInSlot function, which by default crashes or hangs the game. Thus it must be correctly overridden. So I made a subclass "Container2" which has the following override:
Code:
    @Override
    public ItemStack transferStackInSlot(EntityPlayer playerIn, int index)
    {
        Slot clickedSlot = this.inventorySlots.get(index);
        ItemStack stack;
        if (clickedSlot.getHasStack())
        {
            ItemStack clickedStack = clickedSlot.getStack();
            stack = clickedStack.copy();
            if (clickedStack.getCount() == 0)
            {
                clickedSlot.putStack(ItemStack.EMPTY);
            }
            else
            {
                clickedSlot.onSlotChanged();
            }

            if (clickedStack.getCount() == stack.getCount())
            {
                return ItemStack.EMPTY;
            }

            clickedSlot.onTake(playerIn, clickedStack);
        }
        return super.transferStackInSlot(playerIn,index);
    }
I can't explain exact logic in this, because I coded it a long time ago, but in short whenever a player Shift+clicks, nothing happens. Thus you can create subclasses of this crash-safe container and implement Shift+clicking later. I've never worried about a user Shift+clicking and getting a crash since then.
 
  • Like
Reactions: ICountFrom0

Pyure

Not Totally Useless
Aug 14, 2013
8,334
7,191
383
Waterloo, Ontario
Thanks dude. Ya know one reason why toddlers pick up language so well? Because they have zero concerns about looking dumb. Don't be afraid to post your BAD code too so we can all learn from it together :)
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
Another "lesson" for beginners/intermediates. Have you ever wanted to detect whether an entity is within player's view? I have, so I dug enderman's code where he detects when a player is looking at him. Its logic is scattered across the class, so I had to gather only relevant code and compile it into a custom AI task. One has to deal with vector math here, as it is needed to calculate a dot product of two vectors: a vector where the player is looking at and a vector which points from player's coordinates to target entity's coordinates. I'm not good at vector math, but eventually I figured out a general function for creatures:
Code:
    public static boolean isInSightOfPlayer(EntityCreature subject)
    {
        for (Entity e : subject.world.loadedEntityList) {
            if(e instanceof EntityPlayer)
            {
                EntityPlayer entityPlayer= (EntityPlayer) e;
                Vec3d vecOne=new Vec3d(subject.posX-entityPlayer.posX,(subject.posY+subject.getEyeHeight())-entityPlayer.posY-entityPlayer.getEyeHeight(),subject.posZ-entityPlayer.posZ);
                Vec3d vecTwo=entityPlayer.getLook(1).normalize();
                vecOne=vecOne.normalize();
                double dotproduct=vecTwo.dotProduct(vecOne);
                if(dotproduct>0.6)
                {
                    if(entityPlayer.canEntityBeSeen(subject))
                    {
                        return true;
                    }
                }
            }
        }
        return false;
    }
The dot product of those normalized vectors reaches almost 1.0 when a player is looking directly at the entity, and is ~0.0 when player's view is 90 degrees away from the entity. If dot product is in range 0.6-1.0, then the entity is approximately in player's view (that can depend on game window width, though). Realistically, a player can not see wider than 180 degrees, so 0.0 is reasonable minimum bound. Here I also utilize built-in EntityLivingBase#canEntityBeSeen function, which just checks whether there is a block between playe's "eyes" and target. It should be placed after dot product calculation though, because it's supposedly more expensive.
 
  • Like
Reactions: Pyure

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
Finally I found a need in item disenchantment utility; I don't like unreliable randomness of librarian trades, while trades of certain professions (Toolsmith, Armorer) can give enchanted stuff, but I often find the items not worthy. I briefly searched for an item disenchantment mod, and found one, but I didn't like its complex mechanics. I'm sure that there are disenchantment blocks in other big mods, but I can't afford to install them on my server with limited resources. So I made a simple disenchanter, which does its thing in exchange for energy and is fully automatable. I'm not sure what the energy cost formula should be though. Currently it just includes multiplication of enchantment's level and rarity, but I suppose it wouldn't be balanced. Just expressing my thoughts; though if someone else has thoughts on this, do reply.
 

ICountFrom0

Forum Addict
Aug 21, 2012
906
1,227
159
Vermont
I would take a look at the mechanics of the twilight forest disenchantment table, and possibly talk to the dev of this mod: https://www.minecraftforum.net/foru...aft-mods/2771389-jglrxavpoks-uncrafting-table

They had managed to make a blacklist/whitelist mechanic work really well, to be very configurable.

If you then produce a version that can be automated with taking exp and power to do things automatically, that would be amazing.
 
  • Like
Reactions: Alexiy

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
A neat trick when you are making a tile entity with multiple item handlers: suppose we have a tile entity with 4 slots - 2 for inputting items and 2 for outputting items. In this case we would have to create 3 item handlers - one that handles insertion, another that handles extraction, another for use in associated Container. But using anonymous classes we can declare input and output handlers inside the tile entity:
Code:
   public class Tile extends TileEntity {

    public ItemStackHandler allItemHandler=new ItemStackHandler(4);

    private ItemStackHandler inserter=new ItemStackHandler(2){
        @Nonnull
        @Override
        public ItemStack insertItem(int slot, @Nonnull ItemStack stack, boolean simulate) {
            if(slot==0 || slot==1)
                return allItemHandler.insertItem(slot,stack,simulate);
            return stack;
        }
    };

    private ItemStackHandler extractor=new ItemStackHandler(2)
    {
        @Nonnull
        @Override
        public ItemStack extractItem(int slot, int amount, boolean simulate) {
            if(slot==0 || slot==1)
                return allItemHandler.extractItem(slot+2, amount, simulate);
            return ItemStack.EMPTY;
        }
    };

    @Override
    public boolean hasCapability(Capability<?> capability, @Nullable EnumFacing facing) {
        if(capability== CapabilityItemHandler.ITEM_HANDLER_CAPABILITY) return true;
        return super.hasCapability(capability, facing);
    }

    @Nullable
    @Override
    public <T> T getCapability(Capability<T> capability, @Nullable EnumFacing facing) {
        if(capability==CapabilityItemHandler.ITEM_HANDLER_CAPABILITY)
        {
            if(facing==EnumFacing.UP) return (T) inserter;
            else if(facing==EnumFacing.DOWN) return (T) extractor;
            else if(facing==null) return (T) allItemHandler;
        }
        return super.getCapability(capability, facing);
    }
}
The anonymous inserter and extractor just delegate the actions to general handler, if requested slots are 0 or 1 (notice the "slot+2" expression in the extractor). This way we don't have to create a separate class for each handler, which saves some time and space, and we don't need to save them to NBT.
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
A lot of time passed since I wrote here, and that is mainly because I joined a modding group which develops a new mod. I've learned a lot while being the main coder there, and recently I happened to play the popular game "Plague Inc". I quite liked its concept and I thought of doing something like that in a mod. Like creating diseases which can develop, spread and affect objects in various ways. I'm planning to make this mod open source. If anyone is interested in joining such project, then let me know. If you wonder about how skilled I am, then take a look at my main mods here - https://minecraft.curseforge.com/members/Alexiy/projects.
The starting conditions are following so far:
  • the mod will be developed for MC 1.12.2;
  • the source code will be stored at Github;
  • mod's theme - implementation of real and unreal diseases that can affect players and other creatures;
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
Well, the mod turns to be more like about diseases AND conditions. So far I've implemented common cold, heat stroke, blood loss, dizziness, leg bone fracture, vision impairment and head concussion. Common cold naturally weakens the player and makes him sneeze or cough; heat stroke gradually weakens him and ultimately kills; blood loss is supposed to start when player suffers a physical damage, and decreases health over time until death; leg bone fracture can happen when player falls - it makes player slow (I also wanted to make it disable jumping, but couldn't find a way); vision loss can happen when a block falls down on player - results in blurry vision; head concussion occurs from explosions - results in motion blur; I enjoyed implementing dizziness - it randomly moves player's field of view, and should happen from various causes, currently - blood loss.
Now planning to do food poisoning.
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
Been a while since I posted here. No one expressed a desire to join my project about diseases. I thought about it and decided to change its theme - it will be just a mod which makes game harder. Currently I've implemented 3 things:
  • animals run away from the player
  • the player can get impaired vision if a block falls on his head
  • the player can get dizzy when affected by explosion
How impaired vision looks like -
dObZn14.png


I would be happy to receive feedback on this one mod, particularly what features could be added, since hard mods have become popular lately.
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
A peek of another thing i'm doing:
JYBLGJg.png


New ravines. I have to write code for them from scratch, because vanilla ravines are restrictive on what you can do with them and the code is hard to understand. Though this way I can make them extensively customizable:
YcYJGvw.png


And I managed to make them curve:
4aXXzYv.png


Man, I'm tired.
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
If you can get lava sourceblocks to generate if the ravine isn't in a water biome, and magma blocks if it is, as options in additon to a scattering of glowstone...? Just tossing out ideas.
Are you asking whether I can make ravine filled with lava if it is not in a water biome and filled with magma if it is?
Glowstone is just for debugging - it shows the path along which ravine was generated.
I am ready to deliver a first version of this, but where to should I upload the files?
 
  • Like
Reactions: ICountFrom0

Brotuulaan

Well-Known Member
Jan 14, 2018
190
38
53
Looks like several of your examples have square ends, whereas vanilla ones often have pointed ends. Is that something you've accounted for? Square ends just don't look natural (yes, I realize how ironic that sounds given the game at hand).

It might also be cool if you could manage to modify ravines' behavior when they intersect with a patch of trees (as in the above image). Perhaps when a tree would have generated on top of the ravine, it instead builds a fallen-tree bridge? Might require you building a couple of standard trees per type so it has some to pick from for generation. And maybe that would have to be limited within so many blocks so you don't have a spot with basically solid fallen trees reaching across the ravine.
 

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
Rounded ends have been implemented since then.
My ravines generate after everything else, and I tried to prevent cases when something gets left floating above them, but solution isn't perfect yet. I also had to handle a case when a ravine generates over a mineshaft, deleting its floor because it's made of stone; the generator detects mineshaft fences and places polished andesite under them. Not perfect yet, too:
S34hALT.png
tXoh8wr.png
 
  • Like
Reactions: ICountFrom0

Alexiy

Well-Known Member
Mar 3, 2014
229
128
68
Riga, Latvia
  • Like
Reactions: ICountFrom0

Golrith

Over-Achiever
Trusted User
Nov 11, 2012
3,834
2,137
248