не помню, правда. но была какая то проблема с выбором TeleportList правильногоНу почему же вынужденная? Можно ведь без этого всего teleport_goto##objectId# или npc_%objectId%_ в байпасах . Его еще и парсить надо.
Из того что я понимаю, L2J нужно было решить проблему привязки байпасов страницы к определенному npc. То есть таким образом можно сделать так что-бы когда байпас прилетит тут же и проверить во первых общялся ли игрок с нпц или нет, ну и дальше этот байпас отправить куда нужно.не помню, правда. но была какая то проблема с выбором TeleportList правильного
Teleport request есть в первом сообщении. Он для другого.я извиняюсь, там да все же скорее не верный bypass в тех исходах.
вот c pts hf "teleport_request" там должен быть в html
Посмотреть вложение 88536
ну на моей картинке три байпаса "teleport_request" "teleporttonpc_" "teleport_"Teleport request есть в первом сообщении. Он для другого.
<html><head><body>
Dimensional Door:
<br>
An endless and mysterious vortex slowly spins before you.
<br>
<br>
<a action="bypass -h teleporttonpc_142">Jump in.</a>
<br>
</body></html>
Спасибо за помощь. На мысль натолкнули.ну на моей картинке три байпаса "teleport_request" "teleporttonpc_" "teleport_"
teleport_request "запрашивает" список телепортов (там запрос улетает в npc сервер)
teleport_ судя по логгерам "Teleport hack!!! char_name[%s]" "Teleport hack or something changed!!! char_name[%s]" и системным сообщениям "Недостаточно аден." "Недостаточно необходимых предметов." наверное и есть телепорт
по teleporttonpc_ затрудняюсь сказать что это но есть например в g_dimension_door_01.htm
HTML:<html><head><body> Dimensional Door: <br> An endless and mysterious vortex slowly spins before you. <br> <br> <a action="bypass -h teleporttonpc_142">Jump in.</a> <br> </body></html>
других байпасов со словом teleport тут нет )
<button align=left icon=teleport action="bypass -h teleport_5077863200_0_57_1209033476_3" msg="811;F;1010004"><fstring>1010004</fstring></button>
bypass -h teleport_5077863200_0_57_1209033476_3
это очередное извращение от корейцев - телепорт к нпс с заданным идпо teleporttonpc_ затрудняюсь сказать что это но есть например в g_dimension_door_01.htm
На примере ПТС скриптов обычного телепортера. Как выглядит цепочка телепорта.Как выглядит структура и bypass динамично формирующегося html для телепорта?
default void RegisterTeleporterType(int type, int level) {
switch (getNASCActor()) {
case Npc npc_actor -> NpcTeleportTypeManager.getInstance().registerTeleporterType(npc_actor, ETeleporterType.list.get(type), level);
case null, default -> Loggers.STATIC.error("Unsupported actor type!", new IllegalArgumentException());
}
}
@Override
public void TELEPORT_REQUESTED(ISharedCreature talker) {
if (talker.transformID() == 111 || talker.transformID() == 112 || talker.transformID() == 124) {
ShowPage(talker, "q194_noteleport.htm");
} else {
TeleportFStr(talker, "Position", ShopName, "", "", "", 57, 1000308, "", "", "", "", "");
}
}
@Override
public void TELEPORT_REQUESTED(ISharedCreature talker) {
if (IsMyLord(talker) || HavePledgePower(talker, ppSetGate) && Castle_GetPledgeId() == talker.pledge_id() && talker.pledge_id() != 0) {
if (talker.transformID() == 111 || talker.transformID() == 112 || talker.transformID() == 124) {
ShowPage(talker, "q194_noteleport.htm");
} else if (Agit_GetDecoLevel(decotype_teleport) == 11) {
TeleportFStr(talker, "Position1", ShopName, "", "", "", 57, 1000308, "", "", "", "", "");
} else if (Agit_GetDecoLevel(decotype_teleport) == 12) {
TeleportFStr(talker, "Position2", ShopName, "", "", "", 57, 1000308, "", "", "", "", "");
} else {
ShowPage(talker, fnFuncDisabled);
}
} else {
ShowPage(talker, fnNoAuthority);
}
}
default void TeleportFStr(ISharedCreature sCreature, String telposlist, String shopname, String byePage, String clsMissMatchPage, String underAdenaPage, int nItemClassID, int fstrItemIndex, String p1, String p2, String p3, String p4, String p5) {
switch (getNASCActor()) {
case Npc npc -> {
if (sCreature instanceof Player talker) {
if (telposlist == null || telposlist.isEmpty()) {
return;
}
int[][] position = TelPosListHolder.getInstance().getTelPosList(npc, telposlist);
Int2ObjectOpenHashMap<String> tpls;
String base_html = """
<html><body>&$556;<br><br><?teleport_list?><br></body></html>
<!--TEMPLET1
<a action="bypass -h teleport_<?list_id?>_<?list_pos?>_<?item_id?>_<?npc_index?>" msg="811;F;<?location?>"><?pos_index?><fstring><?location?></fstring></a><br1>
TEMPLET-->
<!--TEMPLET2
<a action="bypass -h teleport_<?list_id?>_<?list_pos?>_<?item_id?>_<?npc_index?>" msg="811;F;<?location?>"><?pos_index?><fstring><?location?></fstring> - <?count?> <fstring p1="<?p1?>" p2="<?p2?>" p3="<?p3?>" p4="<?p4?>" p5="<?p5?>"><?item_name?></fstring></a><br1>
TEMPLET-->
""";
tpls = LocalFiles.parseTemplate(base_html);
try (var f = talker.openFHTML()) {
f.set(tpls.get(0));
StringBuilder list = new StringBuilder();
final var ldt = LocalDateTime.now();
int day = ldt.getDayOfWeek().getValue();
int hour = ldt.getHour();
boolean lowPrice = nItemClassID == adena && day != 1 && day != 7 && (hour <= 12 || hour >= 22);
int common_level = NpcTeleportTypeManager.getInstance().getTeleporterLevel(npc, ETeleporterType.TELEPORTER_COMMON);
int coretime_level = NpcTeleportTypeManager.getInstance().getTeleporterLevel(npc, ETeleporterType.TELEPORTER_CORETIME);
int free_level = NpcTeleportTypeManager.getInstance().getTeleporterLevel(npc, ETeleporterType.TELEPORTER_FREE);
if (free_level >= 0 && (free_level == 0 || talker.getLevel() <= free_level)) {
String tpl = tpls.get(1);
for (int c = 0; c < position.length; c++) {
list.append(tpl.replace("<?list_id?>", String.valueOf(position.hashCode())).replace("<?list_pos?>", String.valueOf(c)).replace("<?item_id?>", "0").replace("<?npc_index?>", String.valueOf(npc.getObjectId())).replace("<?location?>", String.valueOf(position[c][0])).replace("<?pos_index?>", (c + 1) + ". "));
}
} else if (nItemClassID == olympiad_token && coretime_level >= 0 && (coretime_level == 0 || talker.getLevel() <= coretime_level)) {
String tpl = tpls.get(2);
for (int c = 0; c < position.length; c++) {
list.append(tpl.replace("<?list_id?>", String.valueOf(position.hashCode())).replace("<?list_pos?>", String.valueOf(c)).replace("<?item_id?>", String.valueOf(nItemClassID)).replace("<?npc_index?>", String.valueOf(npc.getObjectId())).replace("<?location?>", String.valueOf(position[c][0])).replace("<?count?>", String.valueOf(position[c][4])).replace("<?item_name?>", String.valueOf(fstrItemIndex)).replace("<?p1?>", p1).replace("<?p2?>", p2).replace("<?p3?>", p3).replace("<?p4?>", p4).replace("<?p5?>", p5).replace("<?pos_index?>", (c + 1) + ". "));
}
} else if (common_level >= 0 && (common_level == 0 || talker.getLevel() <= common_level)) {
String tpl = tpls.get(2);
for (int c = 0; c < position.length; c++) {
list.append(tpl.replace("<?list_id?>", String.valueOf(position.hashCode())).replace("<?list_pos?>", String.valueOf(c)).replace("<?item_id?>", String.valueOf(nItemClassID)).replace("<?npc_index?>", String.valueOf(npc.getObjectId())).replace("<?location?>", String.valueOf(position[c][0])).replace("<?count?>", String.valueOf(position[c][4] / (lowPrice ? 2 : 1))).replace("<?item_name?>", String.valueOf(fstrItemIndex)).replace("<?p1?>", p1).replace("<?p2?>", p2).replace("<?p3?>", p3).replace("<?p4?>", p4).replace("<?p5?>", p5).replace("<?pos_index?>", (c + 1) + ". "));
}
}
f.replace("<?teleport_list?>", list.toString());
f.showNpc(npc);
}
}
}
case null, default -> Loggers.STATIC.error("Unsupported actor type!", new IllegalArgumentException());
}
}
суть оного - заспавнить на сервере 2 нпс которые могут быть только в одном экземпляре - если есть предыдущий спавн, то он удаляется.
ну и потом тупо через их диалоги перемещаться между этими нпс.
Спасибо. Вот такой ответ и ждал. А откуда источник? Или сами исследовали?Так выглядит 1 строчка из списка
Код:<button align=left icon=teleport action="bypass -h teleport_5077863200_0_57_1209033476_3" msg="811;F;1010004"><fstring>1010004</fstring></button>
Это сам байпас
Код:bypass -h teleport_5077863200_0_57_1209033476_3
-h - закроет окно при отправке пакета
teleport - сам хендлер для обработки байпаса
5077863200 - это просто адрес списка позиций в кэше PosList L2Server.exe. По умолчанию кэш ПУСТ, поэтому этот адрес напрямую зависит от того, как рано L2NPC запрашивает конкретный PosList (обычно из-за взаимодействия игрока с NPC).
0 - индекс элемента в PosList
57 - тип / предмет платы за телепорт (что должно быть потреблено — PosList содержит только суммы)
1209033476 - smartId (в жаве objId нпц)
3 - тип телепортации, где 3 означает «новичок (бесплатно)», 1 — стандартный, 2 — ивент coretime
msg="811;F;1010004 - то что появляется при нажатии на строчку телепорта
811 - sysMsgId
F - Fstring = NpcString
1010004 - NpcStringId
Итог - Вы перемещаетесь в локацию (Глудин). Продолжить?
Спасибо за развернутый ответ.Вам важно понять, что те ИИ, которые вы смотрите, это СКРИПТЫ. Обработка логики этих скриптов проходит в NASC-интерпретаторе, который содержит саму логику внутренних функций скрипта. Т.е всякие ShowPage(talker, fnYouAreChaotic);, Teleport(talker, Position, ShopName, "", "", ""); и еще полторы тысячи функций для НПЦ и мейкеров. Не разобрав логику этих функций, вы не сможете ее воспроизвести.
На примере ПТС скриптов обычного телепортера. Как выглядит цепочка телепорта.
1) НПЦ при инициализации NASC скрипта, регается в холдер как телепортер, предоставляя список допустимых видов ТП.
2) После разговора с этим НПЦ, отсылается стартовая страница, которая обычно содержит байпасс teleport_request
3) Когда в клиенте тыкается этот байпасс, обработчик байпассов на стороне сервера триггерит NASC хендлер TELEPORT_REQUESTED в скрипте этого НПЦ, передавая туда определенные аргументы.
4) В большинстве случаев, в хендлере TELEPORT_REQUESTED у телепортеров вызывается функция TeleportFStr, которая формирует страницу телепорта и отсылает ее в клиент в качестве HTML сообщения.
5) Ну и дальше при клике на ТП, клиент шлет байпасс teleport с параметрами, которые интерпретируются на сервере и обрабатываются уже не в наске.
Сначала, когда проходит загрузка NASC-скриптов, актор наска регается как телепортер:
Java:default void RegisterTeleporterType(int type, int level) { switch (getNASCActor()) { case Npc npc_actor -> NpcTeleportTypeManager.getInstance().registerTeleporterType(npc_actor, ETeleporterType.list.get(type), level); case null, default -> Loggers.STATIC.error("Unsupported actor type!", new IllegalArgumentException()); } }
После этого, когда проходит вызов функции телепорта, триггерится хендлер телепорта:
Java:@Override public void TELEPORT_REQUESTED(ISharedCreature talker) { if (talker.transformID() == 111 || talker.transformID() == 112 || talker.transformID() == 124) { ShowPage(talker, "q194_noteleport.htm"); } else { TeleportFStr(talker, "Position", ShopName, "", "", "", 57, 1000308, "", "", "", "", ""); } }
Посмотреть вложение 88539Java:@Override public void TELEPORT_REQUESTED(ISharedCreature talker) { if (IsMyLord(talker) || HavePledgePower(talker, ppSetGate) && Castle_GetPledgeId() == talker.pledge_id() && talker.pledge_id() != 0) { if (talker.transformID() == 111 || talker.transformID() == 112 || talker.transformID() == 124) { ShowPage(talker, "q194_noteleport.htm"); } else if (Agit_GetDecoLevel(decotype_teleport) == 11) { TeleportFStr(talker, "Position1", ShopName, "", "", "", 57, 1000308, "", "", "", "", ""); } else if (Agit_GetDecoLevel(decotype_teleport) == 12) { TeleportFStr(talker, "Position2", ShopName, "", "", "", 57, 1000308, "", "", "", "", ""); } else { ShowPage(talker, fnFuncDisabled); } } else { ShowPage(talker, fnNoAuthority); } }
И вот такая реализация. Тут ты сможешь найти и структуру html и примерную логику:
Java:default void TeleportFStr(ISharedCreature sCreature, String telposlist, String shopname, String byePage, String clsMissMatchPage, String underAdenaPage, int nItemClassID, int fstrItemIndex, String p1, String p2, String p3, String p4, String p5) { switch (getNASCActor()) { case Npc npc -> { if (sCreature instanceof Player talker) { if (telposlist == null || telposlist.isEmpty()) { return; } int[][] position = TelPosListHolder.getInstance().getTelPosList(npc, telposlist); Int2ObjectOpenHashMap<String> tpls; String base_html = """ <html><body>&$556;<br><br><?teleport_list?><br></body></html> <!--TEMPLET1 <a action="bypass -h teleport_<?list_id?>_<?list_pos?>_<?item_id?>_<?npc_index?>" msg="811;F;<?location?>"><?pos_index?><fstring><?location?></fstring></a><br1> TEMPLET--> <!--TEMPLET2 <a action="bypass -h teleport_<?list_id?>_<?list_pos?>_<?item_id?>_<?npc_index?>" msg="811;F;<?location?>"><?pos_index?><fstring><?location?></fstring> - <?count?> <fstring p1="<?p1?>" p2="<?p2?>" p3="<?p3?>" p4="<?p4?>" p5="<?p5?>"><?item_name?></fstring></a><br1> TEMPLET--> """; tpls = LocalFiles.parseTemplate(base_html); try (var f = talker.openFHTML()) { f.set(tpls.get(0)); StringBuilder list = new StringBuilder(); final var ldt = LocalDateTime.now(); int day = ldt.getDayOfWeek().getValue(); int hour = ldt.getHour(); boolean lowPrice = nItemClassID == adena && day != 1 && day != 7 && (hour <= 12 || hour >= 22); int common_level = NpcTeleportTypeManager.getInstance().getTeleporterLevel(npc, ETeleporterType.TELEPORTER_COMMON); int coretime_level = NpcTeleportTypeManager.getInstance().getTeleporterLevel(npc, ETeleporterType.TELEPORTER_CORETIME); int free_level = NpcTeleportTypeManager.getInstance().getTeleporterLevel(npc, ETeleporterType.TELEPORTER_FREE); if (free_level >= 0 && (free_level == 0 || talker.getLevel() <= free_level)) { String tpl = tpls.get(1); for (int c = 0; c < position.length; c++) { list.append(tpl.replace("<?list_id?>", String.valueOf(position.hashCode())).replace("<?list_pos?>", String.valueOf(c)).replace("<?item_id?>", "0").replace("<?npc_index?>", String.valueOf(npc.getObjectId())).replace("<?location?>", String.valueOf(position[c][0])).replace("<?pos_index?>", (c + 1) + ". ")); } } else if (nItemClassID == olympiad_token && coretime_level >= 0 && (coretime_level == 0 || talker.getLevel() <= coretime_level)) { String tpl = tpls.get(2); for (int c = 0; c < position.length; c++) { list.append(tpl.replace("<?list_id?>", String.valueOf(position.hashCode())).replace("<?list_pos?>", String.valueOf(c)).replace("<?item_id?>", String.valueOf(nItemClassID)).replace("<?npc_index?>", String.valueOf(npc.getObjectId())).replace("<?location?>", String.valueOf(position[c][0])).replace("<?count?>", String.valueOf(position[c][4])).replace("<?item_name?>", String.valueOf(fstrItemIndex)).replace("<?p1?>", p1).replace("<?p2?>", p2).replace("<?p3?>", p3).replace("<?p4?>", p4).replace("<?p5?>", p5).replace("<?pos_index?>", (c + 1) + ". ")); } } else if (common_level >= 0 && (common_level == 0 || talker.getLevel() <= common_level)) { String tpl = tpls.get(2); for (int c = 0; c < position.length; c++) { list.append(tpl.replace("<?list_id?>", String.valueOf(position.hashCode())).replace("<?list_pos?>", String.valueOf(c)).replace("<?item_id?>", String.valueOf(nItemClassID)).replace("<?npc_index?>", String.valueOf(npc.getObjectId())).replace("<?location?>", String.valueOf(position[c][0])).replace("<?count?>", String.valueOf(position[c][4] / (lowPrice ? 2 : 1))).replace("<?item_name?>", String.valueOf(fstrItemIndex)).replace("<?p1?>", p1).replace("<?p2?>", p2).replace("<?p3?>", p3).replace("<?p4?>", p4).replace("<?p5?>", p5).replace("<?pos_index?>", (c + 1) + ". ")); } } f.replace("<?teleport_list?>", list.toString()); f.showNpc(npc); } } } case null, default -> Loggers.STATIC.error("Unsupported actor type!", new IllegalArgumentException()); } }
Это реализация для ХФ.
Ага, это для ГМской трансформы скиллы. Ну и unique npc в целом так работает. Не только на ворота. Есть NPC определенные, которые могут быть только в одном экземпляре призваны, т.к повторный вызов ломает NASC. Т.е второй экземпляр тупо не заспавится, если существует первый. Их не много, но они есть.
Посмотреть вложение 88540
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?
We use cookies and similar technologies for the following purposes:
Do you accept cookies and these technologies?