Skip to content

[1.21.8] End Portal not triggering world change teleport event #389

@L8-Alphine

Description

@L8-Alphine

Expected behavior

When a player enters a natural end portal in the overworld they are teleported to the end.

Observed/Actual behavior

No message, no error, no teleport action. Unsure the reason why the event is not firing.

Steps/models to reproduce

  • Run 1.21.8 Folia
  • Locate Stronghold
  • Activate the portal
  • Enter the Portal

No event fired.

Plugin and Datapack List

[21:36:15 INFO]: ℹ Server Plugins (69):
[21:36:15 INFO]: Paper Plugins (12):
[21:36:15 INFO]:  - CrazyCrates, EconomyShopGUI-Premium, ExtraGreetings, FancyHolograms, FancyNpcs, FeatherMorph, Nexo, NexoAddon, ServiceIO, UltimateMobCoins
[21:36:15 INFO]:  Worlds, ZealousChat
[21:36:15 INFO]: Bukkit Plugins (57):
[21:36:15 INFO]:  - ajLeaderboards, AuraSkills, Aurora, AuroraCollections, AuroraLevels, AuroraQuests, AxAFKZone, AxEnvoy, AxGraves, AxPlayerWarps
[21:36:15 INFO]:  AxRewards, AxTrade, AxVaults, BetterModel, BetterRTP, BreweryX, ChunkEntityLimiter, Chunker, CrazyAuctions, CrazyEnchantments
[21:36:15 INFO]:  CrazyVouchers, DeluxeMenus, DiscordSRV, Duels, Essentials, EssentialsSpawn, GrimAC, Kryptonite, Lands, LevelledMobs
[21:36:15 INFO]:  LiteAnnouncer, LiteBans, LotterySix, LuckPerms, mc-ChatGame, NBTAPI, OpenJS, packetevents, PlaceholderAPI, Plan
[21:36:15 INFO]:  ProtocolLib, PvPManager, RoseLoot, SayanVanish, *Sentry, SmartSpawner, StellarProtect, SupremeTags, TAB, Tebex
[21:36:15 INFO]:  Terra, ViaBackwards, ViaVersion, Votifier, VotingPlugin, WorldEdit, WorldGuard

Folia version

This server is running Folia version 1.21.8-3-ver/1.21.8@890349d (2025-08-22T02:10:39Z) (Implementing API version 1.21.8-R0.1-SNAPSHOT)
You are running the latest version
Previous version: 1.21.8-2-4ed5b66 (MC: 1.21.8)

Other

Worldlimit Script:

//!PlaceholderAPI
// world_gate.js — Limit world entry by Playtime and/or Placeholder value
// OpenJS: registerEvent / addCommand; Folia-safe; no global polling.

(function () {
  // =============== Config (EDIT ME) ===============
  var CFG = {
    debug: true,

    // Default when a world has no matching rule:
    defaultPolicy: "allow",      // "allow" | "deny"
    // If PlaceholderAPI fails for a PAPI check:
    policyIfPapiMissing: "deny", // "allow" | "deny"

    // Where to send players when blocked (used for join/respawn or when a TP is denied)
    fallback: { world: "world", useWorldSpawn: true, x: 0, y: 80, z: 0, yaw: 0.0, pitch: 0.0 },

    // Permissions
    perms: { admin: "worldgate.admin", bypass: "worldgate.bypass" },

    // Messages
    messages: {
      denied:  "§cYou cannot enter §e{world}§c yet.",
      bounced: "§eYou were moved to a safe area.",
      status:  "§7{world} §8→ {result} §8(rule={rule} logic={logic})"
    },

    /*
     * Rules (FIRST MATCH WINS, top→bottom)
     * Each rule targets one or more worlds (name list OR regex) and combines conditions with AND/OR.
     *
     * Condition types supported NOW:
     *  1) Playtime (server statistic OR a PAPI placeholder if you prefer)
     *     { type:"playtime", source:"stat"|"papi", key?: "%placeholder%", unit:"hours"|"minutes"|"ticks",
     *       op:"gte|gt|lte|lt|eq", value:number }
     *     - source:"stat"  → uses Bukkit Statistic.PLAY_ONE_MINUTE (ticks)
     *     - source:"papi"  → reads your placeholder (e.g. %statistic_time_played%) then converts by unit
     *
     *  2) PAPI (generic string/number check)
     *     { type:"papi", key:"%your_placeholder%", op:"eq|contains|regex|in|gte|gt|lte|lt", value:"..." }
     *
     * Examples included below.
     */
    rules: [
      // Example A: Nether requires ≥ 5 hours playtime (server statistic)
      {
        name: "nether-10-level",
        worlds: ["world_nether"],
        logic: "AND",
        conditions: [
        //   { type: "playtime", source: "stat", unit: "hours", op: "gte", value: 5 }
            { type:"papi", key:"%aurora_level%", op:"gte", value:10 }
        ],
        deniedMessage: "§cYou need to be at least level §e10§c to enter §e{world}§c."
      },

      // Example B: Mining world needs either ≥ 10h OR a PAPI flag "unlocked"
    //   {
    //     name: "mining-10h-or-flag",
    //     worlds: ["mining"],
    //     logic: "OR",
    //     conditions: [
    //       { type: "playtime", source: "stat", unit: "hours", op: "gte", value: 10 },
    //       { type: "papi", key: "%myplugin_world_mining%", op: "eq", value: "unlocked" }
    //     ],
    //     deniedMessage: "§cYou need §e10h§c playtime or unlock flag to enter §e{world}§c."
    //   },

      // Example C: Any world matching ^event_.* needs a specific group via PAPI
    //   {
    //     name: "event-worlds-group",
    //     worldsPattern: "^event_.*$",
    //     logic: "AND",
    //     conditions: [
    //       { type: "papi", key: "%luckperms_primary_group%", op: "in", value: "member,moderator,admin" }
    //     ],
    //     deniedMessage: "§cOnly §emember+§c can enter this event world."
    //   },

      // Example D: If you prefer PAPI for playtime (e.g., %statistic_time_played% → ticks):
      {
        name: "end-2h-papi",
        worlds: ["world_the_end"],
        logic: "AND",
        conditions: [
          { type:"playtime", source:"papi", key:"%aurora_level%", unit:"hours", op:"gte", value:20 }
        ],
        deniedMessage: "§cYou need to be at least level §e20§c to enter §e{world}§c."
      }
    ]
  };

  // =============== Internals ===============
  var Bukkit = org.bukkit.Bukkit;
  var Statistic = org.bukkit.Statistic;

  function slog(level, msg){
    try { var L = Bukkit.getLogger(); if (L) { (level==="warn"?L.warning(msg):L.info(msg)); return; } } catch(_){}
    try { java.lang.System.err.println(msg); } catch(_){}
  }
  function log(msg){ if (CFG.debug) slog("info", "[WorldGate] " + msg); }
  function warn(msg){ slog("warn", "[WorldGate] " + msg); }

  function uuidOf(p){ return p.getUniqueId().toString(); }
  function isNumeric(s){ return /^-?\d+(\.\d+)?$/.test((""+s).trim()); }
  function toNum(s){ try { return parseFloat((""+s).trim()); } catch(_){ return NaN; } }

  function getFallbackLocation(spec){
    try {
      var f = spec || CFG.fallback;
      var w = Bukkit.getWorld(f.world);
      if (!w) return null;
      if (f.useWorldSpawn) return w.getSpawnLocation();
      return new org.bukkit.Location(w, f.x, f.y, f.z, f.yaw, f.pitch);
    } catch(e){ return null; }
  }

  // ---- Playtime helpers ----
  function getPlaytimeTicks_stat(player){
    try { return player.getStatistic(Statistic.PLAY_ONE_MINUTE) || 0; } catch(e){ return 0; }
  }
  function getPlaytimeTicks_papi(player, placeholder){
    try {
      var raw = "" + PlaceholderAPI.parseString(player, placeholder);
      if (!/^\d+$/.test(raw)) return 0;
      return java.lang.Long.parseLong(raw);
    } catch(e){ return 0; }
  }
  function convertTicks(valueTicks, unit){
    if (unit === "ticks")   return valueTicks * 1.0;
    if (unit === "minutes") return valueTicks / 20.0 / 60.0;
    // default hours
    return valueTicks / 20.0 / 3600.0;
  }

  // ---- Rule matching ----
  function matchRule(worldName){
    for (var i=0;i<CFG.rules.length;i++){
      var R = CFG.rules[i];
      if (R.worlds && R.worlds.indexOf(worldName) >= 0) return R;
      if (R.worldsPattern){
        try {
          if (java.util.regex.Pattern.compile(R.worldsPattern).matcher(worldName).find()) return R;
        } catch(_){}
      }
    }
    return null;
  }

  // ---- Condition evaluation (ONLY playtime + papi) ----
  function evalCond(player, C){
    var type = (C.type||"playtime").toLowerCase();

    if (type === "playtime"){
      var src  = (C.source||"stat").toLowerCase();
      var unit = (C.unit||"hours").toLowerCase();
      var ticks = (src === "papi")
        ? getPlaytimeTicks_papi(player, C.key || "%statistic_time_played%")
        : getPlaytimeTicks_stat(player);
      var value = convertTicks(ticks, unit); // numeric value in requested unit

      var op = (C.op||"gte").toLowerCase();
      var need = toNum(C.value);
      if (isNaN(need)) return false;

      if (op==="gte") return value >= need;
      if (op==="gt")  return value >  need;
      if (op==="lte") return value <= need;
      if (op==="lt")  return value <  need;
      if (op==="eq")  return Math.abs(value - need) < 1e-9;
      return false;
    }

    if (type === "papi"){
      var raw = null;
      try { raw = "" + PlaceholderAPI.parseString(player, C.key); } catch(e){ raw = null; }
      if (raw === null || raw === undefined || raw === "null") {
        return (CFG.policyIfPapiMissing.toLowerCase() === "allow");
      }

      var op = (C.op||"eq").toLowerCase();
      // numeric ops
      if (op==="gte"||op==="gt"||op==="lte"||op==="lt"||op==="eq"){
        var a = toNum(raw), b = toNum(C.value);
        if (isNaN(a) || isNaN(b)) return false;
        if (op==="gte") return a>=b;
        if (op==="gt")  return a>b;
        if (op==="lte") return a<=b;
        if (op==="lt")  return a<b;
        if (op==="eq")  return Math.abs(a-b) < 1e-9;
      }
      // string ops
      var s = raw.trim().toLowerCase();
      if (op==="contains") return s.indexOf((""+C.value).trim().toLowerCase()) >= 0;
      if (op==="regex"){ try { return java.util.regex.Pattern.compile(""+C.value).matcher(raw).find(); } catch(e){ return false; } }
      if (op==="in"){
        var parts = (""+C.value).split(/\s*,\s*/).map(function(t){return t.toLowerCase();});
        return parts.indexOf(s) >= 0;
      }
      // default equals (string)
      return s === (""+C.value).trim().toLowerCase();
    }

    return false;
  }

  function evalRule(player, rule){
    if (!rule || !rule.conditions || rule.conditions.length===0) return true;
    var AND = (""+(rule.logic||"AND")).toUpperCase() === "AND";
    if (AND){
      for (var i=0;i<rule.conditions.length;i++){
        if (!evalCond(player, rule.conditions[i])) return false;
      }
      return true;
    } else {
      for (var j=0;j<rule.conditions.length;j++){
        if (evalCond(player, rule.conditions[j])) return true;
      }
      return false;
    }
  }

  // ---- Bypass (perm or runtime) ----
  var RUNTIME_BYPASS = new java.util.HashSet();
  function isBypassed(p){
    try { if (p.hasPermission(CFG.perms.bypass)) return true; } catch(_){}
    return RUNTIME_BYPASS.contains(uuidOf(p));
  }

  // ---- Gate actions ----
  function denyMessage(p, w, rule){
    try {
      var msg = (rule && rule.deniedMessage) || CFG.messages.denied;
      p.sendMessage(msg.replace("{world}", w));
    } catch(_){}
  }
  function bouncedMessage(p){ try { p.sendMessage(CFG.messages.bounced); } catch(_){ } }

  function checkAllowedOrBounce(player, targetWorld, mode){
    // mode: "teleport"|"portal"|"respawn"|"join"
    if (!player || !player.isOnline()) return true;
    if (isBypassed(player)) return true;

    var rule = matchRule(targetWorld);
    if (!rule){
      var allow = CFG.defaultPolicy.toLowerCase() === "allow";
      if (!allow) {
        if (mode==="teleport" || mode==="portal"){ denyMessage(player, targetWorld, null); return false; }
        var fb0 = getFallbackLocation(); if (fb0){ try{ player.teleport(fb0); bouncedMessage(player);}catch(e){} }
        else denyMessage(player, targetWorld, null);
        return false;
      }
      return true;
    }

    var ok = false;
    try { ok = evalRule(player, rule); } catch(e){ warn("eval error: " + e); ok = false; }

    if (ok) return true;

    if (mode==="teleport" || mode==="portal"){
      denyMessage(player, targetWorld, rule);
      return false;
    }
    var fb = getFallbackLocation(rule.fallback || CFG.fallback);
    if (fb) { try { player.teleport(fb); bouncedMessage(player); } catch(e){ warn("bounce fail: " + e); } }
    else denyMessage(player, targetWorld, rule);
    return false;
  }

  // =============== Events (OpenJS) ===============
  registerEvent("org.bukkit.event.player.PlayerTeleportEvent", function(ev){
    try {
      var to = ev.getTo(), from = ev.getFrom();
      if (!to || !to.getWorld()) return;
      var toW = to.getWorld().getName();
      if (from && from.getWorld() && from.getWorld().getName() === toW) return; // same world
      if (!checkAllowedOrBounce(ev.getPlayer(), toW, "teleport")) ev.setCancelled(true);
    } catch(e){ warn("Teleport hook: " + e); }
  });

  registerEvent("org.bukkit.event.player.PlayerPortalEvent", function(ev){
    try {
      var to = ev.getTo(); if (!to || !to.getWorld()) return;
      var toW = to.getWorld().getName();
      if (!checkAllowedOrBounce(ev.getPlayer(), toW, "portal")) ev.setCancelled(true);
    } catch(e){ warn("Portal hook: " + e); }
  });

  registerEvent("org.bukkit.event.player.PlayerRespawnEvent", function(ev){
    try {
      var to = ev.getRespawnLocation(); if (!to || !to.getWorld()) return;
      var toW = to.getWorld().getName();
      if (!checkAllowedOrBounce(ev.getPlayer(), toW, "respawn")) {
        var fb = getFallbackLocation();
        if (fb) ev.setRespawnLocation(fb);
      }
    } catch(e){ warn("Respawn hook: " + e); }
  });

  registerEvent("org.bukkit.event.player.PlayerJoinEvent", function(ev){
    try {
      var w = ev.getPlayer().getWorld(); if (!w) return;
      checkAllowedOrBounce(ev.getPlayer(), w.getName(), "join");
    } catch(e){ warn("Join hook: " + e); }
  });

  // =============== Commands (OpenJS) ===============
  addCommand("worldgate", {
    onCommand: function(sender, jargs){
      var args = toArray(jargs);
      var sub = (args[0]||"help").toLowerCase();

      function isAdmin(){
        if (sender instanceof org.bukkit.command.ConsoleCommandSender) return true;
        try { return sender.hasPermission(CFG.perms.admin); } catch(_){ return false; }
      }
      function findPlayer(name){
        if (!name||name.length===0) return (sender instanceof org.bukkit.entity.Player) ? sender : null;
        return Bukkit.getPlayerExact(name);
      }

      if (sub === "status"){
        var target = findPlayer(args[1]||"");
        if (!target) { sender.sendMessage("§cPlayer not found."); return; }
        var worldName = (args[2] || (target.getWorld() ? target.getWorld().getName() : "world"));
        var rule = matchRule(worldName);
        var ok = rule ? evalRule(target, rule) : (CFG.defaultPolicy.toLowerCase()==="allow");

        sender.sendMessage("§bWorldGate status for §e"+target.getName()+"§b:");
        sender.sendMessage(" " + CFG.messages.status
          .replace("{world}", worldName)
          .replace("{result}", ok ? "§aALLOWED" : "§cDENIED")
          .replace("{rule}", rule? (rule.name||"#"+(CFG.rules.indexOf(rule)+1)) : "none")
          .replace("{logic}", rule? (rule.logic||"AND") : "-")
        );
        if (rule && rule.conditions && rule.conditions.length){
          sender.sendMessage(" §7conditions:");
          for (var i=0;i<rule.conditions.length;i++){
            var C = rule.conditions[i];
            var pass = evalCond(target, C);
            var tag = (C.type||"playtime");
            if (tag==="papi") tag += ":" + (C.key||"?");
            if (tag==="playtime") tag += "@" + (C.source||"stat");
            sender.sendMessage("  §8- §f"+tag+" §7op=§f"+(C.op||"eq")+" §7val=§f"+(C.value!=null?C.value:"")+
              (C.unit? " §7unit=§f"+C.unit:"") + " §7→ " + (pass?"§aPASS":"§cFAIL"));
          }
        }
        sender.sendMessage(" §7bypass: §f" + (isBypassed(target)));
        return;
      }

      if (sub === "bypass"){
        if (!isAdmin()) { sender.sendMessage("§cNo permission."); return; }
        var onoff = (args[1]||"").toLowerCase();
        var target = findPlayer(args[2]||"");
        if (!target) { sender.sendMessage("§cPlayer not found."); return; }
        var id = uuidOf(target);
        if (onoff==="on"||onoff==="true"||onoff==="1"){
          RUNTIME_BYPASS.add(id);
          sender.sendMessage("§aBypass enabled for §e"+target.getName());
          target.sendMessage("§aWorldGate: bypass enabled.");
        } else if (onoff==="off"||onoff==="false"||onoff==="0"){
          RUNTIME_BYPASS.remove(id);
          sender.sendMessage("§aBypass disabled for §e"+target.getName());
          target.sendMessage("§eWorldGate: bypass disabled.");
        } else {
          sender.sendMessage("§7Usage: /worldgate bypass <on|off> [player]");
        }
        return;
      }

      if (sub === "test"){
        if (!isAdmin()) { sender.sendMessage("§cNo permission."); return; }
        if (args.length<3) { sender.sendMessage("§7Usage: /worldgate test <player> <world>"); return; }
        var target = findPlayer(args[1]); var worldName = args[2];
        if (!target) { sender.sendMessage("§cPlayer not found."); return; }
        var rule = matchRule(worldName);
        var ok = rule ? evalRule(target, rule) : (CFG.defaultPolicy.toLowerCase()==="allow");
        sender.sendMessage("§bTest: §7world=§e"+worldName+" §7→ "+(ok?"§aALLOWED":"§cDENIED")+
                           " §8(rule="+(rule?(rule.name||"#"+(CFG.rules.indexOf(rule)+1)):"none")+")");
        return;
      }

      sender.sendMessage("§6WorldGate:");
      sender.sendMessage(" §f/worldgate status §7[player] [world]");
      sender.sendMessage(" §f/worldgate bypass <on|off> §7[player]");
      sender.sendMessage(" §f/worldgate test <player> <world>");
    },
    onTabComplete: function(sender, jargs){
      var args = toArray(jargs);
      if (args.length===1) return toJavaList(["status","bypass","test"]);
      if (args.length===2 && args[0].toLowerCase()==="bypass") return toJavaList(["on","off"]);
      return toJavaList([]);
    }
  }, CFG.perms.admin);

  slog("info", "[WorldGate] Loaded. Rules=" + CFG.rules.length + ", defaultPolicy=" + CFG.defaultPolicy);
})();

Even when disabled the issue is still there.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions