Jump to content
Search In
  • More options...
Find results that contain...
Find results in...

[1.16.1] Advancement quantity criterion


PlayPrey
 Share

Recommended Posts

Greetings,

 

I am attempting to update an old mod of mine, which added a lot of achievements.

I thought I would start with the simple stuff, namely adding the milestone styled advancements first.

However it turns out triggering an advancement after for example killing 5 zombies is more difficult than expected. 

It appears easy if all the entities for the advancement are different, but I tried the option that obviously wouldn't work- adding 5x of the zombie kill trigger criterion. 

As expected the advancement is still triggered after killing just one. 

 

As far as I know it is not possible to read from the stats easily from the JSON files.

 

I'll admit, i'm a few years behind with this new system of doing things- but I have managed the simple normal things in advancements. 

 

I have been googling about, and it is possible to use custom triggers,- but the tutorial I found said "copy and paste this code"- which didn't work,- and had no documentation to explain things. 

 

Does anyone here have experience with triggering advancements with a quantity requirement? It would be a nice detail if I could use the counter that's visible on some advancements (like discover all biomes). 

Edited by PlayPrey

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

You can call CriteriaTriggers.register from FMLCommonSetupEvent (use DeferredWorkQueue, the registry is just a HashMap and thus not threadsafe).

Then you have to somehow trigger your trigger, in your case probably from LivingDeathEvent. But you also need the amount of entities killed. Thankfully Minecraft already tracks entity statistics for you, so you could hook into those as well.

Link to comment
Share on other sites

Thank you, what you're saying does make sense- however it's been 5 years since I worked with stat tracking and things have changed.

In addition to that it's been a while since I worked with Java + Forge ( I normally code C# applications ). 

 

You don't have to of course, but would you be willing to aid me with some slight tea-spoon styled assistance? 

 

Just to test I added 

LOGGER.info("Common setup");

into the 

private void setup(final FMLCommonSetupEvent event)

 

In my head this would trigger after the mod has loaded, or during?  However it never logged my text into the console. 

 

It's kinda embarrassing to be in this early beginner stages again, hehe. 

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

12 hours ago, PlayPrey said:

However it never logged my text into the console.

Show the class your event is in, I suspect you didn't call IEventBus#addListener(Consumer<T extends Event>) or annotate it with @SubscribeEvent.

Edited by Novârch

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Link to comment
Share on other sites

This is not going to be pretty, brace yourself. 

 

I saw that I am adding the listener (From the forge example), but that didn't work- so I tried adding subscribe event,- which didn't work because it was a private method.

This is the latest attempt at a... log message.... 

 

package no.pcservicetbg.advancementextreme;

import net.minecraft.advancements.CriteriaTriggers;
import net.minecraft.block.Block;
import net.minecraft.block.Blocks;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.RegistryEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.InterModComms;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.event.lifecycle.FMLClientSetupEvent;
import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent;
import net.minecraftforge.fml.event.lifecycle.InterModEnqueueEvent;
import net.minecraftforge.fml.event.lifecycle.InterModProcessEvent;
import net.minecraftforge.fml.event.server.FMLServerStartingEvent;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.util.stream.Collectors;

// The value here should match an entry in the META-INF/mods.toml file
@Mod("advancementextreme")
public class AdvancementExtreme
{


    // Directly reference a log4j logger.
    private static final Logger LOGGER = LogManager.getLogger();

    public AdvancementExtreme() {
        // Register the setup method for modloading
        FMLJavaModLoadingContext.get().getModEventBus().addListener(this::setup);
        // Register the enqueueIMC method for modloading
        FMLJavaModLoadingContext.get().getModEventBus().addListener(this::enqueueIMC);
        // Register the processIMC method for modloading
        FMLJavaModLoadingContext.get().getModEventBus().addListener(this::processIMC);
        // Register the doClientStuff method for modloading
        FMLJavaModLoadingContext.get().getModEventBus().addListener(this::doClientStuff);

        // Register ourselves for server and other game events we are interested in
        MinecraftForge.EVENT_BUS.register(this);
    }

    @SubscribeEvent
    public void setup(final FMLCommonSetupEvent event)
    {
        LOGGER.info("Common SETUP");
    }

    private void doClientStuff(final FMLClientSetupEvent event) {
    }

    private void enqueueIMC(final InterModEnqueueEvent event)
    {
    }

    private void processIMC(final InterModProcessEvent event)
    {
        // some example code to receive and process InterModComms from other mods
    }




    // You can use SubscribeEvent and let the Event Bus discover methods to call
    @SubscribeEvent
    public void onServerStarting(FMLServerStartingEvent event) {
        // do something when the server starts
    }


    // You can use EventBusSubscriber to automatically subscribe events on the contained class (this is subscribing to the MOD
    // Event bus for receiving Registry Events)
    @Mod.EventBusSubscriber(bus=Mod.EventBusSubscriber.Bus.MOD)
    public static class RegistryEvents {
        @SubscribeEvent
        public static void onBlocksRegistry(final RegistryEvent.Register<Block> blockRegistryEvent) {
            // register a new block here
            LOGGER.info("HELLO from Register Block");
        }
    }
}

 

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

1 minute ago, PlayPrey said:

This is not going to be pretty, brace yourself. 

 

I saw that I am adding the listener (From the forge example), but that didn't work- so I tried adding subscribe event,- which didn't work because it was a private method.

This is the latest attempt at a... log message.... 

 

That should work, is it possible you just missed the message? Pro tip if you're logging in a spammy log, do something like this to ensure you can see you're message,

for (int i = 0; i < 1000; i++)
  LogManager.getLogger().debug("Common setup fired!");

Please remove it before publishing your mod though.

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Link to comment
Share on other sites

So I added the loop, and it still didn't show up at all in the log. 

This is odd, the example mod did come with logging in the other methods (which I removed) and they worked fine. 

 

@SubscribeEvent
    public void setup(final FMLCommonSetupEvent event)
    {
        for(int i = 0; i < 1000; i++)
        {
            LOGGER.info("Common SETUP");
        }
    }

 

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

6 hours ago, PlayPrey said:

So I added the loop, and it still didn't show up at all in the log. 

This is odd, the example mod did come with logging in the other methods (which I removed) and they worked fine. 

 


@SubscribeEvent
    public void setup(final FMLCommonSetupEvent event)
    {
        for(int i = 0; i < 1000; i++)
        {
            LOGGER.info("Common SETUP");
        }
    }

 

You certainly don't need the @SubscribeEvent annotation there. This method doesn't have it in the example mod either.

 

Also, can you stop telling everytime to everyone here and there that you've had experience with Java eternity ago? You've been saying that for four years already and it doesn't seem like you improved during recent activity or had any experience before that at all. Maybe you was as bad as you are now and that's all. Really doesn't worth mentioning. Pls stop.

Edited by Rinkashikachi
Link to comment
Share on other sites

20 hours ago, Rinkashikachi said:

You certainly don't need the @SubscribeEvent annotation there. This method doesn't have it in the example mod either.

For some reason it doesn't work with or without the @SubscribeEvent 

Quote

Also, can you stop telling everytime to everyone here and there that you've had experience with Java eternity ago? You've been saying that for four years already and it doesn't seem like you improved during recent activity or had any experience before that at all. Maybe you was as bad as you are now and that's all. Really doesn't worth mentioning. Pls stop.

Also, I apologize- I don't know why I felt the need to assure people "knew" about my past "experience". I guess I thought it would help people when they knew more about who they were dealing with.

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

So good news, the events always worked- it's just the logging part that didn't. 

Tested with something else, like teleporting/hurting/etc the player and it all triggered fine. 

 

Now, I do wish there was a simple way of granting an advancement from a string value (as ID). 

For example: player.getAdvancement("mod:folder/jsonfile") but sadly that method doesn't exist. 

 

Does anyone know of a way like that? Or is the best way to actually create a new Criterion and reference to that from the json files? 

 

I looked into the built in triggers in the game and got a little stuck, because a lot of the functions in them have names like "p_230241_2" all over them. 

That just makes me confused. But if that's the way I am willing to at least give it a shot. 

Just don't want to go about it the completely wrong way from the get go. 

 

Thanks so far. 

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

Take a look at

AdvancementCommand

, it seems helpful.

Edited by Novârch

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Link to comment
Share on other sites

2 hours ago, Novârch said:

Take a look at


AdvancementCommand

, it seems helpful.

It certainly does, however I am missing out on something simple again.

The AdvancementCommand way requires the ServerPlayerEntity,- and I have to admit I struggle getting the current player as an ServerPlayerEntity.

 

Just to test I did something like

sp = mc.player;
ServerPlayerEntity mp = sp.getServer().getPlayerList().getPlayerByUsername("Dev");

Considering the debug player name is Dev I thought it might work. I know one shouldn't do getPlayerByUsername but end goal here was just to see a working result, before optimizing it. 

However this code just returns null + throws a NullPointerException. 

 

So in short, how do I get the _current_ player as an ServerPlayerEntity? :) This mod has no intention of having any multiplayer functions,- so the mod itself doesn't require knowing anything about other players. However Minecraft is basically a server run game now so it's probably unavoidable. 

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

4 minutes ago, PlayPrey said:

mc.player

Minecraft#player is a ClientPlayerEntity, your NP comes from you trying to get it's server. 

 

5 minutes ago, PlayPrey said:

This mod has no intention of having any multiplayer functions

That is not a good thing to do, unless your mod is client-side, you should always support multiplayer. If your mod is client-side, I'm pretty sure you can't do this, the server doesn't trust the client.

Where are you running this code? Context is important.

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Link to comment
Share on other sites

2 minutes ago, Novârch said:

Where are you running this code? Context is important.

I'd like to point out, it's purely for testing purposes but I am running the code on every EntityDeath from a;

@SubscribeEvent
    public void onDeath(LivingDeathEvent event)
    {
        if(sp == null) sp = mc.player;

        mp = sp.getServer().getPlayerList().getPlayerByUsername("Dev");
    }

This has no intention of staying in a final product. 

The mc variable gets assigned to from the mods initializer as

mc = Minecraft.getInstance();

The variables are stored in the class itself. 

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

2 hours ago, PlayPrey said:

This mod has no intention of having any multiplayer functions

Then you must stay purely client side (visuals, etc.).

Single player is running a server. You cannot ignore multiplayer.

There is no "current player". You have to decide which player you want. Formulate it in English, then we can help you translate it into code. But there is no "current" player.

Link to comment
Share on other sites

1 hour ago, diesieben07 said:

Then you must stay purely client side (visuals, etc.).

Single player is running a server. You cannot ignore multiplayer.

There is no "current player". You have to decide which player you want. Formulate it in English, then we can help you translate it into code. But there is no "current" player.

By current I simply mean yourself. In the same way as "Minecraft.player" returns yourself, but I want to get the ServerPlayerEntity version. If I am playing on a server with 100 players, I still only want myself to be affected. I hope that made it more clear? The reason I need the server version of myself is because Advancements seems to be completely server-side driven. 

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

Just now, diesieben07 said:

The server has no concept of "yourself". The server only has a list of players.

I am aware of this, but I still want a way to find _me_ out of these players. 

I remember you or someone else mentioning finding players using Uuid,- can I somehow get my own Uuid- then check that upon the list of players to find me?

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

6 minutes ago, PlayPrey said:

can I somehow get my own Uuid- then check that upon the list of players to find me?

Maybe, but it is still recommended you mod normally and in a way your mod can work with multiple players.

 

14 minutes ago, PlayPrey said:

If I am playing on a server with 100 players

Nope, advancements are server-side, you cannot modify them from the client. Why? Because the client is a liar.

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Link to comment
Share on other sites

You keep saying "me". The server has no concept of this.

When there is a LivingDeathEvent you can get the source of the damage, which might be an entity and might even be a player. At this point the server just sees "ah, one of the connected players killed an entity". There is no concept of "me" and no possibility for there to be such a concept on the server.

Link to comment
Share on other sites

1 minute ago, Novârch said:

Maybe, but it is still recommended you mod normally and in a way your mod can work with multiple players.

 

1 minute ago, diesieben07 said:

You keep saying "me". The server has no concept of this.

OK, so what I might be realizing here, is that instead of keeping track of myself,- it should keep track of every player. (I feel like this means the host must have the mod installed and the mod won't work on servers, like the old achievement system did- but that's fine for now.)

 

So.. What I have learned, is that my thought process was wrong. Advancements are server-side and shouldn't be messed with client-side. 

 

I have currently successfully retrieved the ServerPlayerEntity of who's killing by simply using 

ServerPlayerEntity spe = (ServerPlayerEntity)event.getSource().getTrueSource();

 

And it works fine. I guess I could store the players in a list to keep track of things in. 

We're making progress :)

 

Next task is to try and find a advancement I have made in a json file,- I don't have time right now to try out different things- but I'll throw out a question real quick:

Do you recommend reading every single advancement in the game and matching names or something, or do you know a way to straight up get a advancement like:

Advancement advancement = Advancement.getFromID("modid:folder1/jsonfilename"); 

I know this code doesn't exist, but if you know of a similar method? I feel like I am close to my goal, that is if the AdvancementCommand way works as it should. :)

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

11 minutes ago, PlayPrey said:

I have currently successfully retrieved the ServerPlayerEntity of who's killing by simply using 


ServerPlayerEntity spe = (ServerPlayerEntity)event.getSource().getTrueSource();

Have you checked instanceof, whether you're on the server with World#isRemote? These are potential issues.

It's sad how much time mods spend saying "x is no longer supported on this forum. Please update to a modern version of Minecraft to receive support".

Link to comment
Share on other sites

13 minutes ago, PlayPrey said:

OK, so what I might be realizing here, is that instead of keeping track of myself,- it should keep track of every player. (I feel like this means the host must have the mod installed and the mod won't work on servers, like the old achievement system did- but that's fine for now.)

Of course it would work on servers, it just needs to be installed on the server. But this is the case even with the old achievement system.

Achievements are not client-side.

 

As for triggering the advancements, you need to look into how the vanilla triggers are implemented.

Link to comment
Share on other sites

Hey, guess what? Got it working- it was surprisingly simple. I saw in the PlayerPredicate class how one finds an advancement via a ResourceLocation and I stole the grant advancement code from AdvancementCommand- and it worked just fine. Advancements get granted via code as well now. Thank you. 

 

However, before I dig myself a hole that I can't get out of, 

3 hours ago, Novârch said:

Have you checked instanceof, whether you're on the server with World#isRemote? These are potential issues.

I don't think so, currently this is my code. It isn't clean yet but thats OK for now.

@SubscribeEvent
    public void onDeath(LivingDeathEvent event)
    {
        ResourceLocation rl = new ResourceLocation("advancementextreme", "slayer/zombie/5zombie");

        if(event.getSource().getTrueSource() instanceof ServerPlayerEntity)
        {
            ServerPlayerEntity spe = (ServerPlayerEntity)event.getSource().getTrueSource();

            PlayerAdvancements pa = spe.getAdvancements();
            AdvancementManager am = spe.getServer().getAdvancementManager();

            Advancement test = am.getAdvancement(rl);

            applyToAdvancement(spe, test);
        }
    }

	protected boolean applyToAdvancement(ServerPlayerEntity player, Advancement advancementIn) {
        AdvancementProgress advancementprogress = player.getAdvancements().getProgress(advancementIn);
        if (advancementprogress.isDone()) {
            return false;
        } else {
            for(String s : advancementprogress.getRemaningCriteria()) {
                player.getAdvancements().grantCriterion(advancementIn, s);
            }

            return true;
        }
    }

I am doing an instanceof check but no World#isRemote check,- I'll have to look into that. 

 

Thank you both a ton so far :)

Intel(R) Core(TM) i7-8700K

32GB DDR4 2400MHz 

NVIDIA GeForce RTX 2080 8GB

Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share



×
×
  • Create New...

Important Information

By using this site, you agree to our Privacy Policy.