When a player enters a natural end portal in the overworld they are teleported to the end.
No message, no error, no teleport action. Unsure the reason why the event is not firing.
No event fired.
[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
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)
//!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.
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
No event fired.
Plugin and Datapack List
Folia version
Other
Worldlimit Script:
Even when disabled the issue is still there.