Resolved Custom BypassHandler does not work.

HuzarO

Vassal
Customer
Hi,

I have created custom `ScriptBypassHandler` and `ScriptItemHandler`, the item works properly, it displays the html chat, but whenever I click the button which should trigger my custom bypass handler, it does nothing. No error in console, no `handle` method being called. Here is my code:

Java:
package com.l2horizon.CustomQuestsExt.handlers.item;

import handler.items.ScriptItemHandler;
import l2.gameserver.model.Playable;
import l2.gameserver.model.Player;
import l2.gameserver.model.base.ClassId;
import l2.gameserver.model.items.ItemInstance;
import l2.gameserver.network.l2.s2c.NpcHtmlMessage;

public class FirstClassChangeItem extends ScriptItemHandler {

    @Override
    public int[] getItemIds() {
        return new int[] { 91706 };
    }

    @Override
    public boolean useItem(Playable playable, ItemInstance item, boolean arg2) {
        if (playable instanceof Player player) {
            // Check if player is in base class (level 1)
            if (player.getClassId().getLevel() != 1) {
                player.sendMessage("This scroll can only be used for first class change!");
                return false;
            }

            // Check if player meets level requirement
            if (player.getLevel() < 20) {
                player.sendMessage("You must be at least level 20 to change your class!");
                return false;
            }

            // Show class change HTML
            showClassChangeHTML(player);

            return true;
        }

        return false;
    }

    private void showClassChangeHTML(Player player) {
        final NpcHtmlMessage html = new NpcHtmlMessage(5);

        final StringBuilder sb = new StringBuilder(500);
        sb.append("<html><body><center>");
        sb.append("<font color=\"LEVEL\">First Class Change</font><br1>");
        sb.append("Select your new class:<br>");
        sb.append("<font color=\"FFAA00\">Cost: 100,000 Adena</font><br1>");

        final ClassId currentClassId = player.getClassId();

        // Collect available classes
        java.util.List<ClassId> availableClasses = new java.util.ArrayList<>();
        for (ClassId cid : ClassId.VALUES) {
            // Only show level 2 classes (first class change)
            if (cid.getLevel() != 2) {
                continue;
            }

            // Check if this class is a valid child of current class
            if (validateClassId(currentClassId, cid)) {
                availableClasses.add(cid);
            }
        }

        if (availableClasses.isEmpty()) {
            sb.append("No available classes found for your character.<br>");
        } else {
            sb.append("<table width=270 border=0 cellspacing=0 cellpadding=1>");

            int count = 0;
            for (ClassId cid : availableClasses) {
                String className = ClassId.getClassById(cid.getId()).name();

                // Format class name: split by underscore and capitalize each word
                String[] words = className.toLowerCase().split("_");
                StringBuilder formattedName = new StringBuilder();
                for (String word : words) {
                    if (word.length() > 0) {
                        formattedName.append(Character.toUpperCase(word.charAt(0)))
                                .append(word.substring(1))
                                .append(" ");
                    }
                }
                className = formattedName.toString().trim();

                if (count % 2 == 0) {
                    sb.append("<tr>");
                }

                sb.append("<td width=135 align=center>")
                        .append("<button value=\"").append(className)
                        .append("\" action=\"bypass _first_class_change_scroll ").append(cid.getId())
                        .append("\" width=130 height=20 back=\"L2UI_CT1.Button_DF_Down\" fore=\"L2UI_CT1.Button_DF\">")
                        .append("</td>");

                count++;
                if (count % 2 == 0) {
                    sb.append("</tr>");
                }
            }

            // Close row if odd number of items
            if (count % 2 != 0) {
                sb.append("<td width=135></td></tr>");
            }

            sb.append("</table>");
        }

        sb.append("</center></body></html>");
        html.setHtml(sb.toString());

        player.sendPacket(html);
    }

    /**
     * Returns true if class change is possible
     *
     * @param oldCID current player ClassId
     * @param newCID new ClassId
     * @return true if class change is possible
     */
    private static boolean validateClassId(ClassId oldCID, ClassId newCID) {
        if (newCID == null) {
            return false;
        }

        if (oldCID == newCID.getParent()) {
            return true;
        }

        return false;
    }
}

and

Java:
package com.l2horizon.CustomQuestsExt.handlers.bypass;

import handler.bypass.ScriptBypassHandler;
import l2.gameserver.model.Player;
import l2.gameserver.model.base.ClassId;
import l2.gameserver.model.instances.NpcInstance;
import l2.gameserver.model.items.ItemInstance;
import l2.gameserver.network.l2.s2c.NpcHtmlMessage;

/**
 * Bypass handler for first class change scroll. Handles
 * "_first_class_change_scroll" bypass commands to perform class changes.
 */
public class FirstClassChangeBypassHandler extends ScriptBypassHandler {

    private static final int FIRST_CLASS_CHANGE_SCROLL_ID = 91706;
    private static final int ADENA_ID = 57;
    private static final long ADENA_COST = 100000;

    @Override
    public void handle(Player player, NpcInstance npc, String bypass, String params) {
        System.out.println("handle: " + bypass + ", " + params);
        String classIdStr = params.trim();

        if (classIdStr.isEmpty()) {
            return;
        }

        try {
            int newClassId = Integer.parseInt(classIdStr);

            // Check if player has the First Class Change Scroll
            ItemInstance scroll = player.getInventory().getItemByItemId(FIRST_CLASS_CHANGE_SCROLL_ID);
            if (scroll == null) {
                player.sendMessage("You need a First Class Change Scroll to change your class!");
                return;
            }

            // Check if player has enough Adena
            ItemInstance adena = player.getInventory().getItemByItemId(ADENA_ID);
            if (adena == null || adena.getCount() < ADENA_COST) {
                player.sendMessage("You need " + ADENA_COST + " Adena to change your class!");
                return;
            }

            // Validate player can do first class change
            if (player.getClassId().getLevel() != 1) {
                player.sendMessage("You are not eligible for first class change!");
                return;
            }

            if (player.getLevel() < 20) {
                player.sendMessage("You must be at least level 20 to change your class!");
                return;
            }

            // Validate the selected class
            ClassId newClass = ClassId.getClassById(newClassId);
            if (newClass == null || newClass.getLevel() != 2) {
                player.sendMessage("Invalid class selection!");
                return;
            }

            // Check if this is a valid class change
            if (!validateClassId(player.getClassId(), newClass)) {
                player.sendMessage("You cannot change to this class!");
                return;
            }

            // Perform the class change
            if (checkAndChangeClass(player, newClassId)) {
                // Delete the scroll and Adena from inventory
                player.getInventory().destroyItem(scroll, 1L);
                player.getInventory().destroyItemByItemId(ADENA_ID, ADENA_COST);

                player.sendMessage("Congratulations! You have changed to " + getFormattedClassName(newClass) + "!");

                // Show success HTML
                final NpcHtmlMessage html = new NpcHtmlMessage(5);
                html.setHtml("<html><body><center>First Class Change:<br><br>" + "Congratulations!<br>"
                        + "You have successfully changed to <font color=\"LEVEL\">" + getFormattedClassName(newClass)
                        + "</font>!<br><br>" + "Your new powers await you!<br>" + "</center></body></html>");
                player.sendPacket(html);
            } else {
                player.sendMessage("Failed to change class. Please contact an administrator.");
            }

        } catch (NumberFormatException e) {
            player.sendMessage("Invalid class selection!");
        }
    }

    /**
     * Returns true if class change is possible
     *
     * @param oldCID current player ClassId
     * @param newCID new ClassId
     * @return true if class change is possible
     */
    private static boolean validateClassId(ClassId oldCID, ClassId newCID) {
        if (newCID == null) {
            return false;
        }

        if (oldCID == newCID.getParent()) {
            return true;
        }

        return false;
    }

    /**
     * Performs the class change
     *
     * @param player     the player
     * @param newClassID the new class ID
     * @return true if successful
     */
    private static boolean checkAndChangeClass(Player player, int newClassID) {
        final ClassId currentClassID = player.getClassId();

        // For first class change, must be level 1 and at least level 20
        if (currentClassID.getLevel() != 1 || player.getLevel() < 20) {
            return false;
        }

        player.setClassId(newClassID, false, false);
        player.broadcastCharInfo();

        return true;
    }

    /**
     * Formats the class name for display
     *
     * @param classId the class ID
     * @return formatted class name
     */
    private static String getFormattedClassName(ClassId classId) {
        String className = classId.name();

        String[] words = className.toLowerCase().split("_");
        StringBuilder formattedName = new StringBuilder();
        for (String word : words) {
            if (word.length() > 0) {
                formattedName.append(Character.toUpperCase(word.charAt(0))).append(word.substring(1)).append(" ");
            }
        }

        return formattedName.toString().trim();
    }

    @Override
    public boolean requiresNpc() {
        return false;
    }

    @Override
    public boolean requiresNpcCheck() {
        return false;
    }

    @Override
    public String[] getBypassPrefixes() {
        return new String[] { "_first_class_change_scroll " };
    }
}

What I'm doing wrong? What I'am missing?
 
I have latest 1847 revision of gameserver, but new ScriptBypassHandler does not work.

Hey @Deazer could you please check it out?

Thank you.
 
I have latest 1847 revision of gameserver, but new ScriptBypassHandler does not work.

Hey @Deazer could you please check it out?

Thank you.
return new String[] { "_first_class_change_scroll " };

remove space after _scroll { "_first_class_change_scroll" };


Everything works without any problems, you are just not careful with your code and don't debug.
 
I have copied your first example from guide at https://lucera2.com/threads/guide-creating-bypass-handlers.8824/

Java:
package handler.bypass;

import l2.gameserver.model.Player;
import l2.gameserver.model.instances.NpcInstance;

public class MyBypassHandler extends ScriptBypassHandler
{
  @Override
  public void handle(Player player, NpcInstance npc, String bypass, String params)
  {
    // Your logic here
    player.sendMessage("Bypass works! Params: " + params);
  }

  @Override
  public String[] getBypassPrefixes()
  {
    return new String[]{"_mybypass"};
  }
}

and this is my NPC html to test:
HTML:
<a action="bypass -h _mybypass">Test</a><br1>
<a action="bypass _mybypass">Test1</a>

but it does not work, it does not send any message to the player
 
<a action="bypass -h _mybypass 1">Test</a>

because getBypassPrefixes


You did not read the guide correctly. 1 Prefix
 
Checked with this, and still the same issue.

HTML:
<a action="bypass -h _mybypass 1">Test</a><br1>
<a action="bypass _mybypass 1">Test1</a>

I have also added some logging to see if this bypass is loaded:

Java:
public MyBypassHandler() {
    super();

    System.out.println("MyBypassHandler: onLoad()");
}

And the message `MyBypassHandler: onLoad()` is shown in the console, but it still does not work :/
 
Apparently, some extension of yours is overriding my changes. Remove all third-party extensions and try again. As this system works correctly and is used by many project participants
 
Back
Top