Looking (HELP) Create a custom ranking pvp/pk system (not show the %contents%

fa1thDEV

Baron
Customer
Hello, this is my first time making a ranking in code, I want make a ranking for show a top 100 like the "score system" the unique diff is what the interface is making with full HTML, my problem is when I put the %content_example% in the HTML, appear like the image, and I dont know what is my error :(

1753747332836.webp

Java:
package services;

import l2.gameserver.data.htm.HtmCache;
import l2.gameserver.database.DatabaseFactory;
import l2.gameserver.model.GameObjectsStorage;
import l2.gameserver.model.Player;
import l2.gameserver.model.base.ClassId;
import l2.gameserver.network.l2.s2c.ShowBoard;
import l2.gameserver.scripts.Functions;
import l2.gameserver.scripts.ScriptFile;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;

public class PvpPkRanking extends Functions implements ScriptFile {

    private static final Logger _log = LoggerFactory.getLogger(PvpPkRanking.class);


    private static class RankingConfig {
        public static final boolean RANKING_ENABLED = true;
        public static final int RANKING_LIMIT = 100;
        public static final long CACHE_UPDATE_INTERVAL_MS = 5 * 60 * 1000;
        public static final String HTML_PATH = "scripts/services/community/pages/ranking.htm";
    }


    private static final TopPvpRecordHolder _pvpHolder = new TopPvpRecordHolder();
    private static final TopPkRecordHolder _pkHolder = new TopPkRecordHolder();

    public void showRanking(String[] args) {
        Player player = getSelf();
        if (player == null) return;

        if (!RankingConfig.RANKING_ENABLED) {
            ShowBoard.separateAndSend(HtmCache.getInstance().getNotNull("scripts/services/community/services/services_disabled.htm", player), player);
            return;
        }

        String html = HtmCache.getInstance().getNotNull(RankingConfig.HTML_PATH, player);


        Collection<TopRecord> pvpRecords = _pvpHolder.getTopRecords();
        int rank = 1;
        for (TopRecord record : pvpRecords) {
            html = replacePlaceholders(html, "pvp", rank, record);
            rank++;
        }

        for (; rank <= RankingConfig.RANKING_LIMIT; rank++) {
            html = clearPlaceholders(html, "pvp", rank);
        }


        Collection<TopRecord> pkRecords = _pkHolder.getTopRecords();
        rank = 1;
        for (TopRecord record : pkRecords) {
            html = replacePlaceholders(html, "pk", rank, record);
            rank++;
        }

        for (; rank <= RankingConfig.RANKING_LIMIT; rank++) {
            html = clearPlaceholders(html, "pk", rank);
        }

        ShowBoard.separateAndSend(html, player);
    }

    private String replacePlaceholders(String html, String type, int rank, TopRecord record) {
        html = html.replace("%name_top" + rank + type + "%", record.getPlayerName());
        html = html.replace("%class_top" + rank + type + "%", record.getClassName());
        html = html.replace("%clan_top" + rank + type + "%", record.getClanName());
        html = html.replace("%ally_top" + rank + type + "%", record.getAllyName());
        html = html.replace("%clan_crest_top" + rank + type + "%", record.getClanCrest());
        html = html.replace("%ally_crest_top" + rank + type + "%", record.getAllyCrest());
        html = html.replace("%amount_top" + rank + type + "%", String.valueOf(record.getTopValue()));
        html = html.replace("%online_top" + rank + type + "%", record.isOnline() ? "<font color=00FF00>Online</font>" : "<font color=FF0000>Offline</font>");
        return html;
    }

    private String clearPlaceholders(String html, String type, int rank) {
        html = html.replace("%name_top" + rank + type + "%", "...");
        html = html.replace("%class_top" + rank + type + "%", "...");
        html = html.replace("%clan_top" + rank + type + "%", "...");
        html = html.replace("%ally_top" + rank + type + "%", "...");
        html = html.replace("%clan_crest_top" + rank + type + "%", "L2UI_CT1.Inventory_DF_CloakSlot_Disable");
        html = html.replace("%ally_crest_top" + rank + type + "%", "L2UI_CT1.Inventory_DF_CloakSlot_Disable");
        html = html.replace("%amount_top" + rank + type + "%", "...");
        html = html.replace("%online_top" + rank + type + "%", "...");
        return html;
    }


    private static class TopRecord implements Comparable<TopRecord> {
        private final int playerObjectId;
        private final String playerName;
        private final int topValue;
        private final String className;
        private final String clanName;
        private final String allyName;
        private final String clanCrest;
        private final String allyCrest;

        public TopRecord(ResultSet rs) throws SQLException {
            this.playerObjectId = rs.getInt("obj_Id");
            this.playerName = rs.getString("char_name");
            this.topValue = rs.getInt("value");
            this.className = ClassId.getClassById(rs.getInt("class_id")).toString();
            this.clanName = rs.getString("clan_name") != null ? rs.getString("clan_name") : "";
            this.allyName = rs.getString("ally_name") != null ? rs.getString("ally_name") : "";
            int clanCrestId = rs.getInt("clan_crest");
            this.clanCrest = clanCrestId > 0 ? "Crest.crest_1_" + clanCrestId : "L2UI_CT1.Inventory_DF_CloakSlot_Disable";
            int allyCrestId = rs.getInt("ally_crest");
            this.allyCrest = allyCrestId > 0 ? "Crest.crest_1_" + allyCrestId : "L2UI_CT1.Inventory_DF_CloakSlot_Disable";
        }

        public boolean isOnline() { return GameObjectsStorage.getPlayer(playerObjectId) != null; }
        public String getPlayerName() { return playerName; }
        public int getTopValue() { return topValue; }
        public String getClassName() { return className; }
        public String getClanName() { return clanName; }
        public String getAllyName() { return allyName; }
        public String getClanCrest() { return clanCrest; }
        public String getAllyCrest() { return allyCrest; }

        @Override
        public int compareTo(TopRecord other) {
            return Integer.compare(other.topValue, this.topValue);
        }

        @Override
        public boolean equals(Object obj) {
            if (this == obj) return true;
            if (obj == null || getClass() != obj.getClass()) return false;
            return playerObjectId == ((TopRecord) obj).playerObjectId;
        }

        @Override
        public int hashCode() {
            return playerObjectId;
        }
    }


    private abstract static class TopRecordHolder {
        private final AtomicReference<List<TopRecord>> cache = new AtomicReference<>(Collections.emptyList());
        private long lastUpdateTime = 0;

        protected abstract Collection<TopRecord> fetchTopDbRecords();

        public Collection<TopRecord> getTopRecords() {
            if (System.currentTimeMillis() - lastUpdateTime > RankingConfig.CACHE_UPDATE_INTERVAL_MS) {
                List<TopRecord> records = new ArrayList<>(fetchTopDbRecords());

                for (Player p : GameObjectsStorage.getAllPlayersForIterate()) {

                }
                Collections.sort(records);
                cache.set(records.subList(0, Math.min(RankingConfig.RANKING_LIMIT, records.size())));
                lastUpdateTime = System.currentTimeMillis();
            }
            return cache.get();
        }
    }


    private static class TopPvpRecordHolder extends TopRecordHolder {
        private static final TopPvpRecordHolder _instance = new TopPvpRecordHolder();
        public static TopPvpRecordHolder getInstance() { return _instance; }

        @Override
        protected Collection<TopRecord> fetchTopDbRecords() {
            return fetchRecords("pvpkills");
        }
    }

    private static class TopPkRecordHolder extends TopRecordHolder {
        private static final TopPkRecordHolder _instance = new TopPkRecordHolder();
        public static TopPkRecordHolder getInstance() { return _instance; }

        @Override
        protected Collection<TopRecord> fetchTopDbRecords() {
            return fetchRecords("pkkills");
        }
    }

    private static List<TopRecord> fetchRecords(String column) {
        List<TopRecord> records = new ArrayList<>();
        String sql = "SELECT c.obj_Id, c.char_name, c." + column + " as value, cs.class_id, cl.clan_name, cl.crest_id as clan_crest, al.ally_name, al.crest_id as ally_crest " +
                "FROM characters c " +
                "JOIN character_subclasses cs ON c.obj_Id = cs.char_obj_id AND cs.isBase = 1 " +
                "LEFT JOIN clan_data cl ON c.clanid = cl.clan_id " +
                "LEFT JOIN alliance al ON cl.ally_id = al.ally_id " +
                "WHERE c.accesslevel = 0 ORDER BY " + column + " DESC LIMIT ?";

        try (Connection con = DatabaseFactory.getInstance().getConnection();
             PreparedStatement stmt = con.prepareStatement(sql)) {
            stmt.setInt(1, RankingConfig.RANKING_LIMIT);
            try (ResultSet rs = stmt.executeQuery()) {
                while (rs.next()) {
                    records.add(new TopRecord(rs));
                }
            }
        } catch (SQLException e) {
            _log.error("Failed to fetch top records for " + column, e);
        }
        return records;
    }

    @Override
    public void onLoad() {
        _log.info("PvpPkRanking: Script loaded successfully.");
    }

    @Override
    public void onReload() {
        _log.info("PvpPkRanking: Script reloaded.");
    }

    @Override
    public void onShutdown() {}
}
 
to me the code looks fine, did you check if this actually returns a collection that is not empty?
Collection<TopRecord> pvpRecords = _pvpHolder.getTopRecords();
(and also the other onewith pk)
 
to me the code looks fine, did you check if this actually returns a collection that is not empty?
Collection<TopRecord> pvpRecords = _pvpHolder.getTopRecords();
(and also the other onewith pk)
I was thinking of making a global placeholder, i.e., one that works for all HTML, but I have no idea how to do that
 
I was thinking of making a global placeholder, i.e., one that works for all HTML, but I have no idea how to do that
Also I would cache the html directly, with all the placeholders resolved, so the replace doesn't have to happen every time the player queries the board
 
Back
Top