Jump to content

Recommended Posts

Posted

Hi there!

It is my first question in this forum, so excuse me if I ask very basic questions. And I don't have a very good English, so forgive me for that too.

I am developing a mod (my first mod, have not experience in mc modding but have it in java programming). My mod is a Pyromaniac mod. I'll explain: the user executes a command (/pyro start) and clicks on some blocks. When finished executes '/pyro stop', and finally executes '/pyro fire' to start all fires at the same time.

Ok. No problem with the commands, they work fine:

Quote

public class PyroCommand {
    
    private static final Logger LOGGER = LogManager.getLogger();
    private PyromaniacMod pyro;
    

    public void register(CommandDispatcher<CommandSourceStack> dispatcher) {
        LiteralArgumentBuilder<CommandSourceStack> PyroCommand = Commands.literal("pyro");
        PyroCommand.then(Commands.literal("start").executes((caca)->{return start();
        }));
        PyroCommand.then(Commands.literal("stop").executes((caca)->{return stop();
        }));
        PyroCommand.then(Commands.literal("fire").executes((caca)->{return fire();
        }));

        dispatcher.register(PyroCommand);
      }
    
    private int start() {
        this.pyro.start();
        return 1;
    }
    
    private int stop() {
        this.pyro.stop();
        return 1;
    }
    
    private int fire() {
        this.pyro.fire();
        return 1;
    }

    public PyromaniacMod getPyro() {
        return pyro;
    }

    public void setPyro(PyromaniacMod pyro) {
        this.pyro = pyro;
    }

 The problem comes when setting the fire blocks. In this test version, all fires start in the .above() block of the targeted position. I can see how fires start, but they don't interact with their fireable neightbours, and nothing of I have done is being saved. I know (or I think I know xD) that the problem is because I am not updating both sides correctly. Could you help me in doing that? Here is my extremely naïve fire function in the PyromaniacMod class:

Quote

 public void fire() {
        player = Minecraft.getInstance().player;
        Level level = Minecraft.getInstance().level;   
        
        if(!this.readyToFire) {
            player.sendMessage(new TextComponent("[Pyro] Run '/pyro fire' again to start the magic."), null);
            this.readyToFire = true;
            return;
        }else {
            player.sendMessage(new TextComponent("[Pyro] Firing. Muuaaaahahahahahaha :D"), null);
            
            for(BlockPos pos:blocs) {
               
                try {                   
                    //level.setBlock(pos.above(), Blocks.FIRE.defaultBlockState(), 3);
                    level.setBlockAndUpdate(pos.above(), Blocks.FIRE.defaultBlockState());
                }catch(Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

Tell me if you need any more code part. And be nice, please ;).

Thaaaaaanks!

Posted
2 hours ago, diesieben07 said:

You are reaching across logical sides. Commands run server side (at least by default, and that is correct here), but this is accessing stuff that is client side. You must use the player from the command context and its level.

Ok. Understood. Now I am saving the player who rightclicks the blocks to fire and finally it works :D. I'm sure there are other things that I'm doing the worst way, but it is easier to learn from a working code xD.

2 hours ago, diesieben07 said:

This is not how you handle exceptions, ever.

I used this as a java standard way of seeing what is crashing. Can you link me some documentation about mc forge exceptions handling?

Thanks!

Posted
4 hours ago, baku said:

Ok. Understood. Now I am saving the player who rightclicks the blocks to fire and finally it works :D. I'm sure there are other things that I'm doing the worst way, but it is easier to learn from a working code xD.

I used this as a java standard way of seeing what is crashing. Can you link me some documentation about mc forge exceptions handling?

Thanks!

the idea is to do checks instead of using a trycatch.

an if statement testing if pos is an air block with a sub check to avoid waterlog / fluid (lava/lava) sources around the targeted block to place the fire would be the way to avoid any problems.   

Minecraft can handle derpy fire (like spawning in mid air & just vanishing after the fact), but being over/near fluid sources can lead to stupidity with how things interact. 

  • Like 1
Posted (edited)

Finally I have a first complete version of the mod. Here are all my classes for if someone considers them useful. I also add some questions below about how to improve it. Here it goes :D

Quote

// MAIN MOD CLASS


@Mod("pyromaniac")
public class PyromaniacMod
{

    public PyromaniacMod() {
               
        MinecraftForge.EVENT_BUS.register(this);
        
//        FMLJavaModLoadingContext.get().getModEventBus().addListener(this::onRegisterCapabilities);
        FMLJavaModLoadingContext.get().getModEventBus().addListener(PyroCapability::register);
        
        MinecraftForge.EVENT_BUS.register(PyroCapabilityAttacher.class);
        
    }
    
    @SubscribeEvent
    public void onCommandsRegister(RegisterCommandsEvent event) {
        
        CommandDispatcher<CommandSourceStack> commandDispatcher = event.getDispatcher();
      
        PyroCommand pyroCommand = new PyroCommand();
        
        pyroCommand.register(commandDispatcher);
        
      
    }   
    
    @SubscribeEvent
    public void onPlayerRightClickBlock(PlayerInteractEvent.RightClickBlock event){
        
        if(PyroCapability.PYRO.isRegistered()) {
            PyroCapabilityInterface pyro = event.getPlayer().getCapability(PyroCapability.PYRO).orElse(null);
            if(pyro.isRunning()) {
                LOGGER.info("Click!");
                event.setCanceled(true);
                
                BlockPos pos = event.getPos();
                Direction dir = event.getFace();
                Player p = event.getPlayer();
                Level l = event.getWorld();
                
                Objectiu obj = new Objectiu(l, p, pos, dir);
                
                if(!pyro.hasObjectiu(obj)) {
                    pyro.addObjectiu(obj);
                    LOGGER.info(pos.toShortString());
                    LOGGER.info(dir.toString());
                }
            }
        }
    }
    
}

Quote

//CUSTOM COMMAND CLASS

public class PyroCommand {
    
    public static void register(CommandDispatcher<CommandSourceStack> dispatcher) {
        
        dispatcher.register(Commands.literal("pyro")
            .then(Commands.literal("start")
                    .executes(PyroCommand::start))
            .then(Commands.literal("stop")
                    .executes(PyroCommand::stop))
            .then(Commands.literal("fire")
                    .executes(PyroCommand::fire))
            );
        
      }
    
    private static int start(CommandContext<CommandSourceStack> context)  throws CommandSyntaxException {
        LOGGER.info(PREFIX+"START");
        Player sender = context.getSource().getPlayerOrException();
        if(PyroCapability.PYRO.isRegistered()) {
            PyroCapabilityInterface pyro = sender.getCapability(PyroCapability.PYRO).orElse(null);
            if(!pyro.isRunning()) {
                pyro.setRunning(true);
                pyro.setReadyToFire(false);
                pyro.clearBlocs();
                sender.sendMessage(new TextComponent(PREFIX + "Pyromaniac selection started. Right click blocks to fire."), sender.getUUID());
            }else {
                sender.sendMessage(new TextComponent(PREFIX + "Pyromaniac selection already started."), sender.getUUID());
            }            
        }else {
            sender.sendMessage(new TextComponent(PREFIX + "[ERROR] You don't have the pyromaniac capability :("), sender.getUUID());
        }
        return 1;
    }
    
    private static int stop(CommandContext<CommandSourceStack> context)  throws CommandSyntaxException {
        LOGGER.info(PREFIX+"STOP");
        
        Player sender = context.getSource().getPlayerOrException();
        if(PyroCapability.PYRO.isRegistered()) {
            PyroCapabilityInterface pyro = sender.getCapability(PyroCapability.PYRO).orElse(null);
            if(pyro.isRunning()) {
                pyro.setRunning(false);
                sender.sendMessage(new TextComponent(PREFIX + "Pyromaniac selection stopped. Please run twice '/pyro fire' to start fires."), sender.getUUID());
            }else {
                sender.sendMessage(new TextComponent(PREFIX + "Pyromaniac selection not started. Please run /pyro start."), sender.getUUID());
            }
        }
        return 1;
    }
    
    private static int fire(CommandContext<CommandSourceStack> context)  throws CommandSyntaxException {
        LOGGER.info(PREFIX+"FIRE");
        
        Player sender = context.getSource().getPlayerOrException();
        if(PyroCapability.PYRO.isRegistered()) {
            PyroCapabilityInterface pyro = sender.getCapability(PyroCapability.PYRO).orElse(null);
            if(!pyro.isReadyToFire()) {
                sender.sendMessage(new TextComponent(PREFIX + "Run '/pyro fire' again to start the magic."), sender.getUUID());
                pyro.setReadyToFire(true);
            }else {
                sender.sendMessage(new TextComponent(PREFIX + "Firing. Muuaaaahahahahahaha :D"), sender.getUUID());
                for(Objectiu obj : pyro.getObjectius()) {
                    Block bloc = Blocks.FIRE;
                    
                    Level l = obj.getLevel();
                    Player p = obj.getPlayer();
                    BlockPos pos = obj.getPos();
                    Direction dir = obj.getDir();
                    
                    BlockPos pos2 = pos.relative(dir);
                    
                    if (BaseFireBlock.canBePlacedAt(l, pos2, dir)) {
                        l.setBlock(pos2, bloc.defaultBlockState(), 3);
                        bloc.setPlacedBy(l, pos2, bloc.defaultBlockState(), p, ItemStack.EMPTY);
                    }
                }
            }
        }
        return 1;
    }
    
}

Quote

//CUSTOM CAPABILITY INTERFACE

public interface PyroCapabilityInterface extends INBTSerializable<CompoundTag> {
    
    boolean running = false;
    boolean readyToFire = false;
    static final ArrayList<Objectiu> blocs = new ArrayList<Objectiu>();    
        
    boolean isRunning();
    void setRunning(boolean running);
    
    boolean isReadyToFire();
    void setReadyToFire(boolean readyToFire);
    
    
    boolean hasObjectiu(Objectiu obj);
    void addObjectiu(Objectiu obj);
    ArrayList<Objectiu> getObjectius();
    
    void clearBlocs();    
}
 

Quote

//CUSTOM CAPABILITY IMPLEMENTATION

public class PyroCapabilityImplementation implements PyroCapabilityInterface {

    private boolean running = false;
    private boolean readyToFire = false;
    private ArrayList<Objectiu> blocs;    

    @Override
    public CompoundTag serializeNBT() {
        LOGGER.info(PREFIX+"SERIALIZE NBT");

        final CompoundTag tag = new CompoundTag();
        
        tag.putBoolean("RUNNING", this.running);
        tag.putBoolean("READYTOFIRE", this.readyToFire);
        
        // TODO
        // Serialize class Objectiu
        
        return tag;
    }

    @Override
    public void deserializeNBT(CompoundTag nbt) {
        LOGGER.info(PREFIX+"DESERIALIZE NBT");

        this.running = nbt.getBoolean("RUNNING");
        this.readyToFire = nbt.getBoolean("READYTOFIRE");
        
        // TODO
        // Deserialize class Objectiu
        
    }

    @Override
    public boolean isRunning() {
        return running;
    }

    @Override
    public void setRunning(boolean running) {
        this.running = running;
    }

    @Override
    public boolean isReadyToFire() {
        return readyToFire;
    }

    @Override
    public void setReadyToFire(boolean readyToFire) {
        this.readyToFire = readyToFire;
    }

    @Override
    public boolean hasObjectiu(Objectiu obj) {
        return this.blocs.contains(obj);
    }

    @Override
    public void addObjectiu(Objectiu obj) {
        this.blocs.add(obj);
    }

    @Override
    public void clearBlocs() {
        this.blocs = new ArrayList<Objectiu>();    
    }

    @Override
    public ArrayList<Objectiu> getObjectius() {
        return this.blocs;
    }

}

Quote

//CUSTOM CAPABILITY ATTACHER

public class PyroCapabilityAttacher {

    private static class PyroCapabilityProvider implements ICapabilityProvider, INBTSerializable<CompoundTag> {
        
        public static final ResourceLocation IDENTIFIER = new ResourceLocation("pyromaniac", "pyro");
        
        private final PyroCapabilityInterface backend = new PyroCapabilityImplementation();
        private final LazyOptional<PyroCapabilityInterface> optionalData = LazyOptional.of(() -> backend);

        
        void invalidate() {
            this.optionalData.invalidate();
        }

        @Nonnull
        @Override
        public <T> LazyOptional<T> getCapability(@Nonnull Capability<T> cap, @Nullable Direction side) {
            return PyroCapability.PYRO.orEmpty(cap, this.optionalData);
        }

        @Override
        public CompoundTag serializeNBT() {
            return this.backend.serializeNBT();
        }

        @Override
        public void deserializeNBT(CompoundTag nbt) {
            this.backend.deserializeNBT(nbt);
            
        }
    }

    @SubscribeEvent
    public static void attach(final AttachCapabilitiesEvent<Entity> event) {
        if(event.getObject() instanceof Player) {
            final PyroCapabilityProvider provider = new PyroCapabilityProvider();
            
            event.addCapability(PyroCapabilityProvider.IDENTIFIER, provider);
            event.addListener(provider::invalidate);
        }
    }

    private PyroCapabilityAttacher() {
    }

}

Quote

//CUSTOM CAPABILITY

public class PyroCapability{

    public static final Capability<PyroCapabilityInterface> PYRO = CapabilityManager.get(new CapabilityToken<>() {});
    
    public static void register(RegisterCapabilitiesEvent event) {
        event.register(PyroCapabilityInterface.class);
    }
    
    private PyroCapability() {
        
    }
}

Quote

//OBJECTIU (target, in English)

public class Objectiu {
    
    private Level level;
    private Player player;
    private BlockPos pos;
    private Direction dir;
    
    public Objectiu(Level level, Player player, BlockPos pos, Direction dir) {
        this.level = level;
        this.player = player;
        this.pos = pos;
        this.dir = dir;
    }

    public Level getLevel() {
        return level;
    }

    public void setLevel(Level level) {
        this.level = level;
    }

    public Player getPlayer() {
        return player;
    }

    public void setPlayer(Player player) {
        this.player = player;
    }

    public BlockPos getPos() {
        return pos;
    }

    public void setPos(BlockPos pos) {
        this.pos = pos;
    }

    public Direction getDir() {
        return dir;
    }

    public void setDir(Direction dir) {
        this.dir = dir;
    }

    // TODO
    // Create equals function
    
//    @Override
//    public boolean equals(Object o) {

//    }
    
    
}

Let's go with some questions:

As I never created a capability, I used this https://gist.github.com/N1K-x/a17812bac9de4cf064baa789e9ccb96a as a basic example. Did I implement it well or is a best way of doing it?

I am checking if the target seleccion is running from the PlayerRightClickEvent, and if so, I'm cancelling the event. Is there a way to tell Minecraft to always disable it for a certain player and not having to do it from inside itself? Easier explain of the question: can I tell MC that a player can't place blocks while the boolean "running" of the mod is true?

I am coding the command execution from the command class. Shall I put the start/stop/fire funcions in the Capability implementation or it is ok to do it here?

 

That's all. I hope my code would be useful as an example of implementing commands or capabilities. And thanks to whoever answers my questions :D.

Edited by baku
my bad English xD

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
Unfortunately, your content contains terms that we do not allow. Please edit your content to remove the highlighted words below.
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.

Announcements



×
×
  • Create New...

Important Information

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