Jump to content

Alpvax

Members
  • Posts

    304
  • Joined

  • Last visited

  • Days Won

    3

Posts posted by Alpvax

  1. 2 hours ago, KollyPop said:

    I'm new to modding.

     

    1.16.1

    If you are new to modding, I would suggest starting with 1.15, not 1.16. 1.16 has only just been released, so there are still bugs and issues being worked out.

     

    You need to subscribe to one of the render events, and render your text inside the event listener, in the same way you would in any other GUI.

  2. 16 hours ago, TheGreyGhost said:

    1 block = 1 bucket (think lava or water being filled into a bucket and back again).

    I don't agree with that logic.

    Yes, 1 bucket of a fluid is 1 block of the fluid (although it actually doesn't quite reach the top, so you could argue that is not true), but there is nothing saying 1 solid block is equivalent to 1 bucket of that material melted down.

    Liquid doesn't have ingots or nuggets, so those solid conversions do not need to apply to them.

    If we keep it as 1 block of a solid becomes a bucket-and-a-bit, all it means is that you don't fit an even number of blocks into your tanks. But I don't see why that is an issue, because once you have converted it to a fluid, whatever you're using it for (machines?) probably doesn't care about the solid amount.

    If you want to transfer exact amounts, use the blocks. They are probably easier to transport anyway (there is a vanilla mechanic for it, and you can usually carry 64 per inventory slot as opposed to the 1 in a bucket).

    • Like 1
  3. 13 hours ago, TheGreyGhost said:

    THe round trip is the problem.  In real life, you get conservation of mass i.e. if you melt the compound then freeze it, you get the same mass as before.

    if your ingot is 144/1000 of a bucket, then converting ingots to buckets and back again gains you several extra ingots.

    I don't see how that is true at all. Why does 1 block have to fit in 1 bucket? If we just accept that 1 block is 1296mb, everything works.

    I don't see why we have to add the restriction that 1 bucket is 1 (solid) block. We can just accept that melting it makes it less dense, therefore bigger.

    • Like 1
  4. Bear in mind that more players use metric systems than imperial (according to minecraft server statistics, and the fact that there are few countries which still use the imperial system).

    It is far easier for users to understand that half a bucket is 500mb, than it is for them to see 648 "atoms" (If that is the approach, I would prefer to use something like "voxellites" which has no real-world bearing than "atoms" which could cause huge confusion for users of chemical mods).

    And to the next argument that you would just display it as "3 1/2 buckets" or 3 1/1296 buckets", that is hugely unnatural for me (and presumably many others).

     

    I'm not sure what the issue with 144mb/ingot is. Why does a melted compound have to fit in the same space as a solid one? It doesn't in real life.

    And as far as cauldrons, I think it's fair enough to just lose 1mb in a cauldron. Who cares about 1/1000 of a bucket of an infinite resource? And for those mods which have finite supplies of water, they usually add their own mechanisms for collecting/storing it, so the cauldron issue is moot.

    • Like 1
    • Thanks 1
  5. On 6/21/2020 at 1:47 PM, _vertig0 said:

    Villagers spawned with other means

    Do you just mean with spawn eggs? Or do commands not work either?

    Vanilla spawn eggs (I believe) have the entity set when they are created, so if you override the entity, I think you also need to override the spawn egg to make it work.

    Disclaimer: I've not actually done it myself, but I believe that is the case.

  6. 56 minutes ago, Tavi007 said:
    
    Set<String> modFolderList = Sets.newHashSet();
    for (final String modFolder : modFolderList)//loop over supported mods 
    {
        Set<String> mobFileList = Sets.newHashSet();
        for (final String mobFile : mobFileList)//loop over supported mobs (not listed mobs will use 'empty' as default)
        {
          ...

     

    You create a new (empty) set, then loop through it. Twice.

    Neither of your loops will do anything, as they are both always going to be empty when the loop is run.

     

    58 minutes ago, Tavi007 said:

    My folder hierarchy looks like this: data/entities/<supported mod>/<mobs from mod>, where <supported mod> should be the mod id. For the vanilla mobs, this will be named 'minecraft'.

    I would suggest including your modid in the path, to stop there being conflicts.

    I would also not limit it to "supported" mods. That way support for third party mods can be added by datapacks (or even included in those mods if your mod becomes popular enough).

    I would recommend using a path such as data/<datapackid>/<your mod id>/entities/<modid>/<mobname>.json

    That would mean that your zombie definition would be at data/<your mod id>/<your mod id>/entities/minecraft/zombie.json

    It means your modid is duplicated, but there is no risk of another mod using the entities path and causing your mod to crash.

  7. As the Optional annotation has now disappeared in favour of capabilities, there is now no way to add optional features to non-TileEntity blocks.

    An example use case would be proper fluid handling for vanilla cauldrons, or handling of different blocks' "heat" values.

     

    It would have to return a function (BlockState, IWorldReader) -> custom capability instance. And couldn't be cached without more work. The capability would also not be able to be serialisable to NBT (obviously non-TEs don't have NBT).

     

    It would effectively be a mapping of blockstate -> capability.

    If we restricted it to readonly capabilities there would be no issues with modders caching the capability and modifying something they shouldn't, but it would for example allow extraction from cauldrons, but not insertion.

    The optimum solution would be for the capabilities to be cached when first accessed (Probably by calling World/Chunk#getBlockCapability(Capability, BlockPos)) which would then be returned every time afterwards (until the position was unloaded).

     

    It would allow ModB to support ModA's X system, without having to inherit from an API which may not exist at runtime.

     

    This post is intended as the starting point of a discussion, if it is an idea that no-one is interested in, that is fine.

    The alternative is to just ship ModA's API with ModB, or at least the interfaces you are implementing in your block, but that has no way of adding capabilities to blocks which you don't own (e.g. vanilla cauldrons, torches).

    • Like 1
  8. 5 minutes ago, BlockyPenguin said:

    If we go one step further, how do they get it so you can step between dimensions without a loading screen or even a lag spike?

    Because the chunk in the other dimension is already loaded, just like when you move from one chunk to the next in the same dimension, or use the teleport command to move to a loaded chunk.

    • Like 1
  9. On 5/20/2020 at 8:13 AM, diesieben07 said:

    There is no reason to use the marker. It does not save you from accessing client only code.

    I understand that (I also understand how to use DistExecutor, and use it when I need to call client side methods).

    My question is is it safe for me to use it as a marker, just something to double check when I'm calling my methods so I know not to call them from the wrong side. Or would javadoc be better?

    Apologies for somewhat hijacking the thread, I won't reply again.

  10. I have a follow up question:

    I have a world capability, and one of the method returns is Optional (or could be @Nullable).

    The LazyOptional#map argument is a NonNullFunction, and ideally I would return empty from the calling function rather than null.

     

    //Capability.class
    public Optional<INetworkNode> getNode(BlockPos pos) {
        return Optional.ofNullable(nodes.get(pos));
    }
    
    //Utility class (I'm happy to change the return type to LazyOptional)
    public <T> Optional<T> ifNetworkNode(Function<INetworkNode, T> callback, BlockPos pos) {
        return getWorld().getCapability(Capabilities.NETWORK_CAPABILITY).map(graph ->
            graph.getNode(pos)
                .map(callback)
                .orElse(null) //<-- How can I unbox this Optional and return (Lazy)Optional.empty from the method instead of null?
            );
        );
    }

     

    Am I doing something wrong? Have I missed something obvious again?

     

    EDIT: yes I was. I just needed to call orElseThrow on the LazyOptional

    return getWorld().getCapability(Capabilities.NETWORK_GRAPH_CAPABILITY).map(graph ->
            graph.getNode(getPos()).map(callback)
        ).orElseThrow(() -> new NullPointerException("World %s did not have network capability attached"));

     

  11. On 5/17/2020 at 7:08 PM, imacatlolol said:

    OnlyIn does literally nothing for modders. It's an internal marker for Forge/FML only, so modders shouldn't use it.

    How true is this? Does it do nothing, or are the methods stripped during build/run? I have used it in the past as a marker for methods which I know must only be used on the client. Does this have a functional use or is it safe to use as a marker?

  12. 1 hour ago, Draco18s said:

    Why would you need to?

    Because many of the vanilla methods which I'm overriding have return values...

     

    I'm dropping this thread now, Lex answered my question, and it seems you're never going to understand what I was attempting to achieve Draco.

  13. 1 hour ago, Draco18s said:

    But what if it doesn't HAVE a value?

    But I know it does, because I just called isPresent. I would expect it to return null/throw an NPE (either would be acceptable).

     

    1 hour ago, Draco18s said:
    
    val = orElse(null)
    if(val != null) { /*code*/ }

     

    is functionally equivalent to

     

    
    ifPresent(val -> { /*code*/ } );

    Except that you cannot return anything from the second approach, which was the entire reasoning behind this query.

  14. 19 hours ago, LexManos said:

    Its exactly as useful as the normal Java Optional.

    Except the normal optional also has the value() method public, saving the orElse call.

     

    19 hours ago, LexManos said:

    Your examples are also pretty bad. The way you would want to do it is like:
     

    
    cap.map(e -> e.amount() * e.amountMultiplier()).orElse(0)

    Yes, the example was in fact bad, and that was helpful, thank you. I didn't even thing of doing all the processing inside the Map function, I have always tried to make them as simple as possible.

  15. 2 hours ago, Novârch said:

    LazyOptional.ifPresent(props -> {}) is what you want, props will be an instance of your capability. Here's an example of how I use it.

     

    2 hours ago, Draco18s said:

    ifPresent() takes a lambda operator. Anything that would go inside your if(val != null) block goes directly into that lambda.

    Yes, I am aware of that method. But you cannot return a value from it. (scope is now lambda scope).

    I was also asking about the isPresent method, not ifPresent.

  16. Is it just me, or is the LazyOptional very awkward to use?

    The isPresent() function seems virtually useless, what does it offer over doing .orElse(null), followed by a null check, because you cannot get the value without using orElse anyway?

     

    It seems that there is no easy way to return a value from a capability that may not be present. For example:

    interface CapabilitySample {
      int amount();
      int amountMultiplier();
    }
    
    //In another class:
    public int getAmount(ICapabilityProvider obj) {
    	LazyOptional<CapabilitySample> cap = obj.getCapability(Capability_Instance);
       
      //************** Option 1 **************
        // Works if you only need a single method from the capability
        return cap.map(CapabilitySample::amount).orElse(0);
        //But what if you need multiple methods? The following (mapping the capability twice) seems like a bad idea:
        return cap.map(CapabilitySample::amount).orElse(0) * cap.map(CapabilitySample::amountMultiplier).orElse(1);
      
      //************** Option 2 **************
        if (cap.isPresent()) {
            //How do I return cap.amount() from here? There is no way to retrieve the cap.value().
            //Do I really have to do the following?
            CapabilitySample value = cap.orElseThrow(()-> new Exception("Pointless exception which can never happen!"));
            return value.amount() * value.amountModifier();
        } else {
            return 0;
        }
    
      //************** Option 3 **************
        //Requires an entire implementation to be written with dummy methods!
        CapabilitySample value = cap.orElse(dummyCapabilitySample);
        return return value.amount() * value.amountModifier();
    }

     

    Am I missing something, or is this a big oversight? Am I using capabilities wrong? Am I not supposed to return values from them, and just go all-in functional?

     

    As an unrelated aside, I also find the capability default implementation factory impossible to use, because invariably I want to use the object I am attaching to to set up the capability.

×
×
  • Create New...

Important Information

By using this site, you agree to our Terms of Use.