From 89d5331c995a785e13f73bae8f6ae18319d74414 Mon Sep 17 00:00:00 2001 From: MobiusDev <8391001+MobiusDevelopment@users.noreply.github.com> Date: Sat, 22 Oct 2016 10:00:43 +0000 Subject: [PATCH] Synced javaengine removal changes from Test to HighFive. --- .../dist/game/config/AdminCommands.xml | 2 + .../dist/game/data/html/admin/zone_editor.htm | 29 + .../data/html/admin/zone_editor_create.htm | 63 ++ .../game/data/scripts/ai/individual/Core.java | 2 +- .../data/scripts/handlers/MasterHandler.java | 2 + .../admincommandhandlers/AdminQuest.java | 9 +- .../admincommandhandlers/AdminZones.java | 580 ++++++++++++++++++ .../handlers/bypasshandlers/QuestLink.java | 11 +- .../Q00146_TheZeroHour.java | 6 + .../javaengine/CompilationException.java | 32 - .../commons/javaengine/JavaCompiler.java | 124 ---- .../commons/javaengine/JavaScriptEngine.java | 447 -------------- .../javaengine/JavaScriptEngineFactory.java | 221 ------- .../commons/javaengine/MemoryClassLoader.java | 123 ---- .../javaengine/MemoryJavaFileManager.java | 162 ----- .../engines/items/DocumentItem.java | 10 +- .../gameserver/enums/PlayerAction.java | 2 + .../instancemanager/QuestManager.java | 29 +- .../gameserver/model/events/EventType.java | 2 + .../character/player/OnPlayerMoveRequest.java | 53 ++ .../gameserver/model/items/L2Armor.java | 6 + .../gameserver/model/items/L2EtcItem.java | 10 +- .../gameserver/model/items/L2Item.java | 79 +-- .../gameserver/model/items/L2Weapon.java | 38 +- .../gameserver/model/quest/Quest.java | 18 +- .../gameserver/model/zone/form/ZoneNPoly.java | 10 + .../clientpackets/MoveBackwardToLocation.java | 14 +- .../serverpackets/AbstractHtmlPacket.java | 5 + .../serverpackets/ExShowTerritory.java | 59 ++ .../scripting/ScriptEngineManager.java | 20 +- .../scripting/java/JavaExecutionContext.java | 20 +- .../com/l2jmobius/gameserver/util/Util.java | 13 + 32 files changed, 979 insertions(+), 1222 deletions(-) create mode 100644 L2J_Mobius_HighFive/dist/game/data/html/admin/zone_editor.htm create mode 100644 L2J_Mobius_HighFive/dist/game/data/html/admin/zone_editor_create.htm create mode 100644 L2J_Mobius_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminZones.java delete mode 100644 L2J_Mobius_HighFive/java/com/l2jmobius/commons/javaengine/CompilationException.java delete mode 100644 L2J_Mobius_HighFive/java/com/l2jmobius/commons/javaengine/JavaCompiler.java delete mode 100644 L2J_Mobius_HighFive/java/com/l2jmobius/commons/javaengine/JavaScriptEngine.java delete mode 100644 L2J_Mobius_HighFive/java/com/l2jmobius/commons/javaengine/JavaScriptEngineFactory.java delete mode 100644 L2J_Mobius_HighFive/java/com/l2jmobius/commons/javaengine/MemoryClassLoader.java delete mode 100644 L2J_Mobius_HighFive/java/com/l2jmobius/commons/javaengine/MemoryJavaFileManager.java create mode 100644 L2J_Mobius_HighFive/java/com/l2jmobius/gameserver/model/events/impl/character/player/OnPlayerMoveRequest.java create mode 100644 L2J_Mobius_HighFive/java/com/l2jmobius/gameserver/network/serverpackets/ExShowTerritory.java diff --git a/L2J_Mobius_HighFive/dist/game/config/AdminCommands.xml b/L2J_Mobius_HighFive/dist/game/config/AdminCommands.xml index 8341117a66..a43e83986c 100644 --- a/L2J_Mobius_HighFive/dist/game/config/AdminCommands.xml +++ b/L2J_Mobius_HighFive/dist/game/config/AdminCommands.xml @@ -26,6 +26,8 @@ + + diff --git a/L2J_Mobius_HighFive/dist/game/data/html/admin/zone_editor.htm b/L2J_Mobius_HighFive/dist/game/data/html/admin/zone_editor.htm new file mode 100644 index 0000000000..1caedfac39 --- /dev/null +++ b/L2J_Mobius_HighFive/dist/game/data/html/admin/zone_editor.htm @@ -0,0 +1,29 @@ + + + Zone Editor + + +
+ + + + + + + +
+
+
+ +
+ +Zones + +
+ + %zones% +
+
+
+ + \ No newline at end of file diff --git a/L2J_Mobius_HighFive/dist/game/data/html/admin/zone_editor_create.htm b/L2J_Mobius_HighFive/dist/game/data/html/admin/zone_editor_create.htm new file mode 100644 index 0000000000..82ec7dc75e --- /dev/null +++ b/L2J_Mobius_HighFive/dist/game/data/html/admin/zone_editor_create.htm @@ -0,0 +1,63 @@ + + + Zone Creator + + +
+ + + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + +
+
+ + + + %pages% + +
+ +Name: %name% + + + + + + + + + + + + + +
#XYZEditTeleDele
+ +%nodes% +
+ + \ No newline at end of file diff --git a/L2J_Mobius_HighFive/dist/game/data/scripts/ai/individual/Core.java b/L2J_Mobius_HighFive/dist/game/data/scripts/ai/individual/Core.java index d689f46022..44dd300f6d 100644 --- a/L2J_Mobius_HighFive/dist/game/data/scripts/ai/individual/Core.java +++ b/L2J_Mobius_HighFive/dist/game/data/scripts/ai/individual/Core.java @@ -97,7 +97,7 @@ public final class Core extends AbstractNpcAI } @Override - public void saveGlobalData() + public void onSave() { saveGlobalQuestVar("Core_Attacked", Boolean.toString(_firstAttacked)); } diff --git a/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/MasterHandler.java b/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/MasterHandler.java index e0e03191b7..31760320fa 100644 --- a/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/MasterHandler.java +++ b/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/MasterHandler.java @@ -128,6 +128,7 @@ import handlers.admincommandhandlers.AdminTvTEvent; import handlers.admincommandhandlers.AdminUnblockIp; import handlers.admincommandhandlers.AdminVitality; import handlers.admincommandhandlers.AdminZone; +import handlers.admincommandhandlers.AdminZones; import handlers.bypasshandlers.Augment; import handlers.bypasshandlers.Buy; import handlers.bypasshandlers.BuyShadowItem; @@ -377,6 +378,7 @@ public class MasterHandler AdminPetition.class, AdminPForge.class, AdminPledge.class, + AdminZones.class, AdminPolymorph.class, AdminPremium.class, AdminPunishment.class, diff --git a/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminQuest.java b/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminQuest.java index 20ec604b83..ba052f887c 100644 --- a/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminQuest.java +++ b/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminQuest.java @@ -40,6 +40,7 @@ import com.l2jmobius.gameserver.util.Util; public class AdminQuest implements IAdminCommandHandler { public static final Logger LOGGER = Logger.getLogger(AdminQuest.class.getName()); + private static final String[] ADMIN_COMMANDS = { "admin_quest_reload", @@ -49,7 +50,7 @@ public class AdminQuest implements IAdminCommandHandler "admin_quest_info" }; - private Quest findScript(String script) + private static Quest findScript(String script) { if (Util.isDigit(script)) { @@ -155,12 +156,11 @@ public class AdminQuest implements IAdminCommandHandler if (listener.getOwner() instanceof Quest) { final Quest quest = (Quest) listener.getOwner(); - if (questNames.contains(quest.getName())) + if (!questNames.add(quest.getName())) { continue; } sb.append("" + quest.getName() + ""); - questNames.add(quest.getName()); } } } @@ -188,10 +188,9 @@ public class AdminQuest implements IAdminCommandHandler final Set listenerTypes = new TreeSet<>(); for (AbstractEventListener listener : quest.getListeners()) { - if (!listenerTypes.contains(listener.getType())) + if (listenerTypes.add(listener.getType())) { events += ", " + listener.getType().name(); - listenerTypes.add(listener.getType()); counter++; } if (counter > 10) diff --git a/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminZones.java b/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminZones.java new file mode 100644 index 0000000000..c6596649e1 --- /dev/null +++ b/L2J_Mobius_HighFive/dist/game/data/scripts/handlers/admincommandhandlers/AdminZones.java @@ -0,0 +1,580 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package handlers.admincommandhandlers; + +import java.awt.Color; +import java.io.File; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.StringTokenizer; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.GeoData; +import com.l2jmobius.gameserver.enums.PlayerAction; +import com.l2jmobius.gameserver.handler.IAdminCommandHandler; +import com.l2jmobius.gameserver.instancemanager.ZoneManager; +import com.l2jmobius.gameserver.model.Location; +import com.l2jmobius.gameserver.model.PageResult; +import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; +import com.l2jmobius.gameserver.model.events.EventType; +import com.l2jmobius.gameserver.model.events.ListenerRegisterType; +import com.l2jmobius.gameserver.model.events.annotations.Priority; +import com.l2jmobius.gameserver.model.events.annotations.RegisterEvent; +import com.l2jmobius.gameserver.model.events.annotations.RegisterType; +import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerDlgAnswer; +import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerMoveRequest; +import com.l2jmobius.gameserver.model.events.returns.TerminateReturn; +import com.l2jmobius.gameserver.model.zone.L2ZoneType; +import com.l2jmobius.gameserver.model.zone.form.ZoneNPoly; +import com.l2jmobius.gameserver.network.serverpackets.ConfirmDlg; +import com.l2jmobius.gameserver.network.serverpackets.ExServerPrimitive; +import com.l2jmobius.gameserver.network.serverpackets.ExShowTerritory; +import com.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage; +import com.l2jmobius.gameserver.util.HtmlUtil; +import com.l2jmobius.gameserver.util.Util; +import com.l2jmobius.util.Rnd; + +import ai.npc.AbstractNpcAI; + +/** + * @author UnAfraid + */ +public class AdminZones extends AbstractNpcAI implements IAdminCommandHandler +{ + private static final Logger _log = Logger.getLogger(AdminPathNode.class.getName()); + private final Map _zones = new ConcurrentHashMap<>(); + + private static final String[] COMMANDS = + { + "admin_zones", + }; + + public AdminZones() + { + super(AdminZones.class.getSimpleName(), "handlers"); + } + + @Override + public boolean useAdminCommand(String command, L2PcInstance activeChar) + { + final StringTokenizer st = new StringTokenizer(command); + final String cmd = st.nextToken(); + switch (cmd) + { + case "admin_zones": + { + if (!st.hasMoreTokens()) + { + buildZonesEditorWindow(activeChar); + return false; + } + final String subCmd = st.nextToken(); + switch (subCmd) + { + case "load": + { + if (st.hasMoreTokens()) + { + String name = ""; + while (st.hasMoreTokens()) + { + name += st.nextToken() + " "; + } + loadZone(activeChar, name.trim()); + } + break; + } + case "create": + { + buildHtmlWindow(activeChar, 0); + break; + } + case "setname": + { + String name = ""; + while (st.hasMoreTokens()) + { + name += st.nextToken() + " "; + } + if (!name.isEmpty()) + { + name = name.substring(0, name.length() - 1); + } + setName(activeChar, name); + break; + } + case "start": + { + enablePicking(activeChar); + break; + } + case "finish": + { + disablePicking(activeChar); + break; + } + case "show": + { + showPoints(activeChar); + final ConfirmDlg dlg = new ConfirmDlg("When enable show territory you must restart client to remove it, are you sure about that?"); + dlg.addTime(15 * 1000); + activeChar.sendPacket(dlg); + activeChar.addAction(PlayerAction.ADMIN_SHOW_TERRITORY); + break; + } + case "hide": + { + final ZoneNodeHolder holder = _zones.get(activeChar.getObjectId()); + if (holder != null) + { + final ExServerPrimitive exsp = new ExServerPrimitive("DebugPoint_" + activeChar.getObjectId(), activeChar.getX(), activeChar.getY(), activeChar.getZ()); + exsp.addPoint(Color.BLACK, 0, 0, 0); + activeChar.sendPacket(exsp); + } + break; + } + case "change": + { + if (!st.hasMoreTokens()) + { + activeChar.sendMessage("Missing node index!"); + break; + } + final String indexToken = st.nextToken(); + if (!Util.isDigit(indexToken)) + { + activeChar.sendMessage("Node index should be int!"); + break; + } + final int index = Integer.parseInt(indexToken); + changePoint(activeChar, index); + break; + } + case "delete": + { + if (!st.hasMoreTokens()) + { + activeChar.sendMessage("Missing node index!"); + break; + } + final String indexToken = st.nextToken(); + if (!Util.isDigit(indexToken)) + { + activeChar.sendMessage("Node index should be int!"); + break; + } + final int index = Integer.parseInt(indexToken); + deletePoint(activeChar, index); + showPoints(activeChar); + break; + } + case "clear": + { + _zones.remove(activeChar.getObjectId()); + break; + } + case "dump": + { + dumpPoints(activeChar); + break; + } + case "list": + { + final int page = Util.parseNextInt(st, 0); + buildHtmlWindow(activeChar, page); + return false; + } + } + break; + } + } + buildHtmlWindow(activeChar, 0); + return false; + } + + private void buildZonesEditorWindow(L2PcInstance activeChar) + { + final StringBuilder sb = new StringBuilder(); + final List zones = ZoneManager.getInstance().getZones(activeChar); + for (L2ZoneType zone : zones) + { + if (zone.getZone() instanceof ZoneNPoly) + { + sb.append(""); + sb.append("" + zone.getName() + ""); + sb.append(""); + } + } + + final NpcHtmlMessage msg = new NpcHtmlMessage(0, 1); + msg.setFile(activeChar.getHtmlPrefix(), "data/html/admin/zone_editor.htm"); + msg.replace("%zones%", sb.toString()); + activeChar.sendPacket(msg); + } + + /** + * @param activeChar + * @param zoneName + */ + private void loadZone(L2PcInstance activeChar, String zoneName) + { + activeChar.sendMessage("Searching for zone: " + zoneName); + final List zones = ZoneManager.getInstance().getZones(activeChar); + L2ZoneType zoneType = null; + for (L2ZoneType zone : zones) + { + if (zone.getName().equalsIgnoreCase(zoneName)) + { + zoneType = zone; + activeChar.sendMessage("Zone found: " + zone.getId()); + break; + } + } + + if ((zoneType != null) && (zoneType.getZone() instanceof ZoneNPoly)) + { + final ZoneNPoly zone = (ZoneNPoly) zoneType.getZone(); + final ZoneNodeHolder holder = _zones.computeIfAbsent(activeChar.getObjectId(), val -> new ZoneNodeHolder()); + holder.getNodes().clear(); + holder.setName(zoneType.getName()); + for (int i = 0; i < zone.getX().length; i++) + { + final int x = zone.getX()[i]; + final int y = zone.getY()[i]; + holder.addNode(new Location(x, y, GeoData.getInstance().getSpawnHeight(x, y, Rnd.get(zone.getLowZ(), zone.getHighZ())))); + } + showPoints(activeChar); + } + } + + /** + * @param activeChar + * @param name + */ + private void setName(L2PcInstance activeChar, String name) + { + if (name.contains("<") || name.contains(">") || name.contains("&") || name.contains("\\") || name.contains("\"") || name.contains("$")) + { + activeChar.sendMessage("You cannot use symbols like: < > & \" $ \\"); + return; + } + _zones.computeIfAbsent(activeChar.getObjectId(), key -> new ZoneNodeHolder()).setName(name); + } + + /** + * @param activeChar + */ + private void enablePicking(L2PcInstance activeChar) + { + if (!activeChar.hasAction(PlayerAction.ADMIN_POINT_PICKING)) + { + activeChar.addAction(PlayerAction.ADMIN_POINT_PICKING); + activeChar.sendMessage("Point picking mode activated!"); + } + else + { + activeChar.sendMessage("Point picking mode is already activated!"); + } + } + + /** + * @param activeChar + */ + private void disablePicking(L2PcInstance activeChar) + { + if (activeChar.removeAction(PlayerAction.ADMIN_POINT_PICKING)) + { + activeChar.sendMessage("Point picking mode deactivated!"); + } + else + { + activeChar.sendMessage("Point picking mode was not activated!"); + } + } + + /** + * @param activeChar + */ + private void showPoints(L2PcInstance activeChar) + { + final ZoneNodeHolder holder = _zones.get(activeChar.getObjectId()); + if (holder != null) + { + if (holder.getNodes().size() < 3) + { + activeChar.sendMessage("In order to visualize this zone you must have at least 3 points."); + return; + } + final ExServerPrimitive exsp = new ExServerPrimitive("DebugPoint_" + activeChar.getObjectId(), activeChar.getX(), activeChar.getY(), activeChar.getZ()); + int index = 1; + for (Location loc : holder.getNodes()) + { + exsp.addPoint("Point: " + index, Color.GREEN, true, loc); + index++; + } + final List list = holder.getNodes(); + for (int i = 1; i < list.size(); i++) + { + final Location prevLoc = list.get(i - 1); + final Location nextLoc = list.get(i); + exsp.addLine("Point " + i + " > " + (i + 1), Color.WHITE, true, prevLoc, nextLoc); + } + exsp.addLine("Point " + list.size() + " > 1", Color.WHITE, true, list.get(list.size() - 1), list.get(0)); + activeChar.sendPacket(exsp); + } + } + + /** + * @param activeChar + * @param index + */ + private void changePoint(L2PcInstance activeChar, int index) + { + final ZoneNodeHolder holder = _zones.get(activeChar.getObjectId()); + if (holder != null) + { + final Location loc = holder.getNodes().get(index); + if (loc != null) + { + enablePicking(activeChar); + holder.setChangingLoc(loc); + } + } + } + + /** + * @param activeChar + * @param index + */ + private void deletePoint(L2PcInstance activeChar, int index) + { + final ZoneNodeHolder holder = _zones.get(activeChar.getObjectId()); + if (holder != null) + { + final Location loc = holder.getNodes().get(index); + if (loc != null) + { + holder.getNodes().remove(loc); + activeChar.sendMessage("Node " + index + " has been removed!"); + if (holder.getNodes().isEmpty()) + { + activeChar.sendMessage("Since node list is empty destroying session!"); + _zones.remove(activeChar.getObjectId()); + } + } + } + } + + /** + * @param activeChar + */ + private void dumpPoints(final L2PcInstance activeChar) + { + final ZoneNodeHolder holder = _zones.get(activeChar.getObjectId()); + if ((holder != null) && !holder.getNodes().isEmpty()) + { + if (holder.getName().isEmpty()) + { + activeChar.sendMessage("Set name first!"); + return; + } + + final Location firstNode = holder.getNodes().get(0); + final StringBuilder sb = new StringBuilder(); + sb.append("" + Config.EOL); + sb.append("" + Config.EOL); + sb.append("\t" + Config.EOL); + for (Location loc : holder.getNodes()) + { + sb.append("\t\t" + Config.EOL); + } + sb.append("\t" + Config.EOL); + sb.append("" + Config.EOL); + try + { + File file = new File(Config.DATAPACK_ROOT, "log/points/" + activeChar.getAccountName() + "/" + holder.getName() + ".xml"); + if (file.exists()) + { + int i = 0; + while ((file = new File(Config.DATAPACK_ROOT, "log/points/" + activeChar.getAccountName() + "/" + holder.getName() + i + ".xml")).exists()) + { + i++; + } + } + if (!file.getParentFile().isDirectory()) + { + file.getParentFile().mkdirs(); + } + Files.write(file.toPath(), sb.toString().getBytes(StandardCharsets.UTF_8)); + activeChar.sendMessage("Successfully written on: " + file.getAbsolutePath().replace(Config.DATAPACK_ROOT.getAbsolutePath(), "")); + } + catch (Exception e) + { + activeChar.sendMessage("Failed writing the dump: " + e.getMessage()); + _log.log(Level.WARNING, "Failed writing point picking dump for " + activeChar.getName() + ":" + e.getMessage(), e); + } + } + } + + @RegisterEvent(EventType.ON_PLAYER_MOVE_REQUEST) + @RegisterType(ListenerRegisterType.GLOBAL_PLAYERS) + @Priority(Integer.MAX_VALUE) + public TerminateReturn onPlayerPointPicking(OnPlayerMoveRequest event) + { + final L2PcInstance activeChar = event.getActiveChar(); + if (activeChar.hasAction(PlayerAction.ADMIN_POINT_PICKING)) + { + final Location newLocation = event.getLocation(); + final ZoneNodeHolder holder = _zones.computeIfAbsent(activeChar.getObjectId(), key -> new ZoneNodeHolder()); + final Location changeLog = holder.getChangingLoc(); + if (changeLog != null) + { + changeLog.setXYZ(newLocation); + holder.setChangingLoc(null); + activeChar.sendMessage("Location " + (holder.indexOf(changeLog) + 1) + " has been updated!"); + disablePicking(activeChar); + } + else + { + holder.addNode(newLocation); + activeChar.sendMessage("Location " + (holder.indexOf(changeLog) + 1) + " has been added!"); + } + // Auto visualization when nodes >= 3 + if (holder.getNodes().size() >= 3) + { + showPoints(activeChar); + } + buildHtmlWindow(activeChar, 0); + + return new TerminateReturn(true, true, false); + } + return null; + } + + @RegisterEvent(EventType.ON_PLAYER_DLG_ANSWER) + @RegisterType(ListenerRegisterType.GLOBAL_PLAYERS) + public void onPlayerDlgAnswer(OnPlayerDlgAnswer event) + { + final L2PcInstance activeChar = event.getActiveChar(); + if (activeChar.removeAction(PlayerAction.ADMIN_SHOW_TERRITORY) && (event.getAnswer() == 1)) + { + final ZoneNodeHolder holder = _zones.get(activeChar.getObjectId()); + if (holder != null) + { + final List list = holder.getNodes(); + if (list.size() < 3) + { + activeChar.sendMessage("You must have at least 3 nodes to use this option!"); + return; + } + + final Location firstLoc = list.get(0); + final ExShowTerritory exst = new ExShowTerritory(firstLoc.getZ() - 100, firstLoc.getZ() + 100); + list.forEach(exst::addVertice); + activeChar.sendPacket(exst); + activeChar.sendMessage("In order to remove the debug you must restart your game client!"); + } + } + } + + @Override + public String[] getAdminCommandList() + { + return COMMANDS; + } + + private void buildHtmlWindow(final L2PcInstance activeChar, final int page) + { + final NpcHtmlMessage msg = new NpcHtmlMessage(0, 1); + msg.setFile(activeChar.getHtmlPrefix(), "data/html/admin/zone_editor_create.htm"); + final ZoneNodeHolder holder = _zones.computeIfAbsent(activeChar.getObjectId(), key -> new ZoneNodeHolder()); + final AtomicInteger position = new AtomicInteger(page * 20); + final PageResult result = HtmlUtil.createPage(holder.getNodes(), page, 20, i -> + { + return "