diff --git a/.gitignore b/.gitignore
index 412f11d8ab..301f14339b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -11,13 +11,17 @@
/tools/MapleArrowFetcher/dist/
/tools/MapleArrowFetcher/nbproject/
-/tools/MapleBossHpBarFetcher/nbproject/
/tools/MapleBossHpBarFetcher/build/
/tools/MapleBossHpBarFetcher/dist/
+/tools/MapleBossHpBarFetcher/nbproject/
-/tools/MapleCashDropFetcher/nbproject/
/tools/MapleCashDropFetcher/build/
/tools/MapleCashDropFetcher/dist/
+/tools/MapleCashDropFetcher/nbproject/
+
+/tools/MapleCodeCouponGenerator/build/
+/tools/MapleCodeCouponGenerator/dist/
+/tools/MapleCodeCouponGenerator/nbproject/
/tools/MapleCouponInstaller/build/
/tools/MapleCouponInstaller/dist/
@@ -25,7 +29,7 @@
/tools/MapleDojoUpdate/build/
/tools/MapleDojoUpdate/dist/
-/tools/MapleDojoUpdate/nbproject/private/
+/tools/MapleDojoUpdate/nbproject/
/tools/MapleEquipmentOmnileveler/build/
/tools/MapleEquipmentOmnileveler/dist/
@@ -59,9 +63,9 @@
/tools/MapleMobBookUpdate/dist/
/tools/MapleMobBookUpdate/nbproject/
-/tools/MapleQuestItemCountFetcher/nbproject/
/tools/MapleQuestItemCountFetcher/build/
/tools/MapleQuestItemCountFetcher/dist/
+/tools/MapleQuestItemCountFetcher/nbproject/
/tools/MapleQuestItemFetcher/build/
/tools/MapleQuestItemFetcher/dist/
@@ -87,5 +91,9 @@
/tools/MapleSkillMakerReagentIndexer/dist/
/tools/MapleSkillMakerReagentIndexer/nbproject/
+/tools/SpiderDropFetcher/build/
+/tools/SpiderDropFetcher/dist/
+/tools/SpiderDropFetcher/nbproject/
+
/out
*.onetoc2
diff --git a/README.md b/README.md
index 6290ff8250..e113154112 100644
--- a/README.md
+++ b/README.md
@@ -22,7 +22,9 @@ Client files & general tools: https://drive.google.com/drive/folders/0BzDsHSr-0V
**Important note about localhosts**: these executables are red-flagged by antivirus tools as __potentially malicious softwares__, this happens due to the reverse engineering methods that were applied onto these software artifacts. Those depicted here have been put to use for years already and posed no harm so far, so they are soundly assumed to be safe.
-Recommended localhost: https://hostr.co/m2bVtnizCtmD
+ Latest localhost: https://hostr.co/m2bVtnizCtmD
+
+ The following list, in bottom-up chronological order, holds information regarding all changes that were applied from the starting localhost used in this development. Some lines have a link attached, that will lead you to a snapshot of the localhost at that version of the artifact. Naturally, later versions holds all previous changes along with the proposed changes.
**Change log:**
@@ -49,10 +51,27 @@ Recommended localhost: https://hostr.co/m2bVtnizCtmD
* MapleSilver's starting on window-mode.
---
-### Development status
+### Development information
Status: __In development (4th round)__.
+#### Mission
+
+With non-profitting means intended, provide nostalgic pre-BB MapleStory players world-wide a quality local server for freestyle entertainment.
+
+#### Vision
+
+By taking the v83 MapleStory as the angular stone, incrementally look forward to improve the gaming experience whilst still retaining the "clean v83" conservative ideal. Also, through reviewing distinguished aspects of the server's behavior that could be classified as a potential server threat, in the long run look for ways to improve or even stabilize some of it's uncertain aspects.
+
+#### Values
+
+* Autonomy, seek self-improvement for tackling issues head-on;
+* Adventurous, take no fear of failures on the path of progress;
+* Light-hearted support, general people out there didn't experience what you've already had;
+* Humility, no matter how good you are, there's no good in boasting yourself over experiences only a few have had;
+
+#### Announcements
+
HeavenMS development achieved an acceptable state-of-the-art and will get into a halt. A heartfelt thanks for everyone that contributed in some way for the progress of this server!
Although development is halted, support for fixing features that were implemented here is still up. You can still actively help us improve the server by issuing pull requests with informative details about what's changing.
@@ -179,6 +198,24 @@ In that case, extract "lolwut.exe" from "lolwut-v0.01.rar" and place it on the M
Important: should the client be refused a connection to the game server, it may be because of firewall issues. Head to the end of this file to proceed in allowing this connection through the computer's firewall. Alternatively, one can deactivate the firewall and try opening the client again.
+---
+### Creating an account and logging in the game
+
+By default, the server source is set to allow AUTO-REGISTERING. This means that, by simply typing in a "Login ID" and a "Password", you're able to create a new account.
+
+After creating a character, experiment typing in all-chat "@commands". This will display all available commands for the current GM level your character has.
+
+To change a character's GM level, make sure that character is not logged in, then:
+
+* Open MySQL Query Browser;
+* Double-click "heavenms" schema;
+* Double click "characters" table;
+* Execute the selected query;
+* Mark "Edit" flag on the MySQL Query Browser UI screen;
+* Locate your character's row on the displayed ResultSet;
+* Edit your character's GM level;
+* Hit APPLY CHANGES.
+
---
### Some notes about WZ/WZ.XML EDITING
diff --git a/docs/feature_list.md b/docs/feature_list.md
index eb64914123..b4ebf0cc30 100644
--- a/docs/feature_list.md
+++ b/docs/feature_list.md
@@ -68,11 +68,14 @@ Player Social Network:
* Automated support for Player NPCs and Hall of Fame.
* Protected concurrently and improved the face expression system, guarding from trivial packet spam and exploits.
* All upgradeable non-cash equipments in inventory with level & EXP information available for read by anyone, given proper visibility.
+* Further improved the existent minigame mechanics: remarkably checking out for no-item match requests, allowing different omok/matchcard match layouts and status update on the player matchbox tooltips.
Cash & Items:
* EXP/DROP/Cosmetic Coupons.
* EXP/DROP coupons now appears as a buff effect when on active time.
+* Code coupons functional, with support for multiple items on the same code.
+* Merged unique ids for pets, rings and cash items, thus solving some cash shop inventory issues.
* Great deal of cash items functional.
* MapleTV mechanics stabilized and separated by world.
* GMS-esque omok/match card drop chances.
@@ -80,7 +83,7 @@ Cash & Items:
* Inventory system properly checks for item slot free space and ownership.
* Storage with "Arrange Items" feature functional.
* Close-quarters evaluation mode for items (sandbox).
-* Further improved Karma scissors mechanics.
+* Further improved Karma scissors & Untradeable items mechanics.
* Spikes on shoes.
* Vega's spell.
* Owl of Minerva.
@@ -116,6 +119,8 @@ Monsters, Maps & Reactors:
* Updated many scripted portals not implementing SFX properly.
* Updated Crimsonwood, World Tour, Nihal Desert and Neo City, enabling quest completion and game progression in these areas.
* Added world maps for Mushroom Castle, World Tour (Singapore, Malaysia and Zipangu) & Ellin Forest areas.
+* Added World Tour and Masteria continents in the world map.
+* Reviewed World Map's town/field tooltips and links from the main world map and Masteria region.
* Giant Cake (anniversary-themed boss) drops Maple equipments, Maple scrolls, summoning bags and many more interesting items.
PQ potentials:
@@ -182,13 +187,14 @@ Custom NPCs:
* Donation Box: automatized item-buyer.
* Coco & Ace of Hearts: C. scroll crafters.
-Admin/GM commands:
+Server Commands:
* Server commands layered by GM levels.
* Spawn Zakum/Horntail/Pinkbean.
* Several new commands.
* Rank command highlighting users either by world or server-wide.
* Revamped command files layout -- thanks Arthur L.
+* Optimized Search command, caching search range contents and added map search functionality.
External tools:
diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt
index 3b31996e77..518f7a095b 100644
--- a/docs/mychanges_ptbr.txt
+++ b/docs/mychanges_ptbr.txt
@@ -1276,30 +1276,30 @@ Otimizado função goto, agora não mais gerando mapas de cidade/mapid a todo us
Melhorado função de disconnect do MapleClient, evitando múltiplos envios de dados do jogador à DB.
Desenhado (todo conteúdo de imagens creditado à Nexon) e implementado novos mapas-mundi referente a regiões de M. Shrine, Showa, CBD e Metropolis/Kampung.
-03 Agosto 2018,
+03 Setembro 2018,
Implementado buffs inexpiráveis.
Corrigido comando resetStats não levando em conta AP's atuais dos jogadores.
Corrigido novo sistema gerenciador de loots removendo a possibilidade de conseguir novos itens de quest após coleta do primeiro item.
-04 Agosto 2018,
+04 Setembro 2018,
Corrigido um flicker na animação do efeito da skill Hurricane.
Ajustado MapleSessionCoordinator, agora verificando HWID's ao invés de contar somente com o remote IP, evitando negação de serviço para usuários de VPNs.
Corrigido dispel normal incorretamente mostrando efeitos aleatórios a outros jogadores, issue apontado por Thora.
Corrigido change job não mostrando efeito a outros jogadores.
Corrigido valores incorretos sendo retirado de jogadores para expansão de guild, issue apontado por Thora.
-05 Agosto 2018,
+05 Setembro 2018,
Adicionado world map em Ellin Forest.
Protegido concorrentemente sistema de fames.
Adicionado ganho de quest points para jogadores que participam de PQs. Reformulado sistema de quest points para viabilizar a nova feature.
Otimizado método de ganho de experiência em equipamentos, agora devidamente cacheado e sem busca em strings no processo.
-08 Agosto 2018,
+08 Setembro 2018,
MapleServerHandler agora é devidamente deregistrado ao finalizar uma instância de channel, eliminando possível foco de memory leak.
Funções playerDead e playerRevive são agora opcionais em EIM scripts.
Nova ferramenta: MapleEquipmentOmnileveler. Permite adicionar e atualizar nodos referentes a níveis para todos os equipamentos. Em jogo, essa adição permite jogadores a ver nível de qualquer equipamento do jogo.
-09 - 10 Agosto 2018,
+09 - 10 Setembro 2018,
Adicionado cálculo de MP ao realizar ataques de mobs.
Aplicado vários pequenos ajustes no handler que lida com movimentação de mobs, procurando assim reduzir ruidos no fluxo do jogo (e.g. mob caindo de foothold).
Corrigido schedules de event instances sendo chamados após finalização da mesma, levando locks já liberados a serem chamados inesperadamente.
@@ -1307,7 +1307,7 @@ Corrigido casos onde jogadores poderiam requisitar instanciação de uma nova PQ
Aplicado reformatação geral no Character.wz, adicionando nodos de level para todos os equipamentos do jogo. Tal mudança permite que todos os equipamentos exponham nível e experiência em todos os equipamentos no inventário.
Implementado cálculo de experiência no lado do servidor que permite mostrar adequadamente EXP dos equipamentos no lado do cliente.
-11 - 19 Agosto 2018,
+11 - 19 Setembro 2018,
Corrigido MP Recovery skill não atuando segundo descrição, além de matando jogador com pouco HP disponível.
Refatorado completamente acessos de stats no código do server.
Refatorado check de Berserk sendo chamado em múltiplas seções do código.
@@ -1326,5 +1326,59 @@ Corrigido alliances retendo informações de guilds que foram desfeitas na DB.
Corrigido alliances não salvando nomes de ranks na DB ao serem criadas.
Adicionado suporte para doors na região de Mushroom Castle.
-24 Agosto 2018,
-Resolvido pequenas inconsistências entre os XMLs e WZs disponibilizados.
\ No newline at end of file
+24 Setembro 2018,
+Resolvido pequenas inconsistências entre os XMLs e WZs disponibilizados.
+Alterado "session write" para "announce", agora devidamente utilizando encapsulamento e lidando com sincronização de serviços.
+
+25 Setembro 2018,
+Corrigido diversas exceções no cash shop congelando próximas ações dos jogadores, somente ficando disponível após sair do mesmo.
+Reimplementado sistema de code coupons no cash shop.
+Removido skills Teleport e Super Dragon Roar redundantes do job SuperGM (jobid 910).
+Adicionado informações detalhadas do estado corrente do buyback, em forma de comando.
+Corrigido desligamento de channel com jogadores dentro do cash shop levando o sistema a lançar algumas exceções.
+
+26 Setembro 2018,
+Aprimorado code coupons, agora permitindo entrega de vários itens no mesmo código.
+Revisado uso de addById no código, não permitindo adicionar itens limpos em slots de itens que possuem marcação de dono.
+Implementado automatização de geração de coupon codes baseado em templates, utilizando arquivo XML para pegar os parâmetros.
+
+29 Setembro 2018,
+Corrigido Ereve faltando suporte para um Door portal.
+Adicionado suporte de Doors para Happyville e Crimsonwood Keep.
+Melhorado aspectos do world map da região de Masteria, adicionando interiores de Crimsonwood Keep no mapa e tooltip para interiores de Haunted House no mapa de Masteria.
+Aprimorado região interior de Crimsonwood Keep, não mais reposicionando jogadores com a funcionalidade forcedReturn do WZ.
+Corrigido arredores de Amoria não retornando jogadores para a cidade ao ressucitar pelo método usual.
+Corrigido petids e ringids misturando valores com cashids de itens comuns, potencialmente causando problemas com os inventários do jogador e do cash shop da conta do mesmo.
+
+30 Setembro 2018,
+Adicionado região de Masteria no World Map.
+Corrigido ligação de Ludibrium para Orbis não sendo mostrado corretamente.
+Melhorado ratio update de HP/MP, agora memorizando ratio recentes ao efetuar ações como modificar status do inventário de itens equipados, entre outros.
+
+01 - 03 Outubro 2018,
+Corrigido diversos problemas com o sistema de login quando o uso do bcrypt está desativado.
+Implementado/melhorado diversas mecânicas dos minigames (do aspecto dos tooltips dos boxes às pontuações visiveis dentro da sala e sistema de pontuação interno único), tornando agora finalmente viável a jogabilidade estendida dessa funcionalidade.
+Adicionado server flag para evitar levelup em equipamentos de cash.
+Melhorado verificação de itens untradeable em vários pontos do código, agora avaliando também status de Karma.
+Adicionado funcionalidade de pesquisa de mapas no comando Search.
+Otimizado comando Search, agora cacheando dados para as pesquisas.
+Corrigido pequenas inconsistências com HPMP ratio recém implementado.
+Revisado drop chance de vários itens de quest e diversos ETC drops de mobs de nível baixo.
+Revisado descrições de várias skills de GM.
+
+04 Outubro 2018,
+Prequest de Zakum agora recompensa 5 Eye of Fire, valor oficial.
+Modificado script de quiz do 3rd job, agora randomizando posicionamento das respostas.
+Modificado damage-over-time em mapas como arredores de El Nath e Aqua Road, agora dando dano a cada 5 segundos, como previsto na versão oficial.
+Corrigido comando Whodrops potencialmente lagando o servidor se comando for spammado por jogadores.
+Protegido concorrentemente handler de interação de jogadores (trades, player shops, minigames, entre outros).
+Melhorado checagem de acesso concorrente em alguns handlers, agora utilizando semáforos para fazer a filtragem de requisições a esses handlers.
+Revertido pet pickup para permitindo coletar itens indiscriminadamente de ataque. Causa de situações onde pet pickup evita ação de ataque de jogador não está claro.
+Modificado timers para diversas expedições, agora com duração máxima entre 1h30 a 2h.
+
+08 Outubro 2018,
+Protegido concorrentemente módulos de chairs.
+
+09 Outubro 2018,
+Corrigido disease seduce não funcionando da forma esperada quando jogador está sentado.
+Movido Dropspider para fora do código-fonte do server, agora estando como uma ferramenta externa.
\ No newline at end of file
diff --git a/handbook/Use.txt b/handbook/Use.txt
index 6dbac5ec5f..36286ebc6a 100644
--- a/handbook/Use.txt
+++ b/handbook/Use.txt
@@ -903,10 +903,10 @@
2048003 - Scroll for Pet Equip. for Jump - Improves jump on pet equip.\nSuccess rate:100%, jump+1
2048004 - Scroll for Pet Equip. for Jump - Improves jump on pet equip.\nSuccess rate:60%, jump+2. The success rate of this scroll can be enhanced by Vega's Spell.
2048005 - Scroll for Pet Equip. for Jump - Improves jump on pet equip.\nSuccess rate:10%, jump+3. The success rate of this scroll can be enhanced by Vega's Spell.
-2048006 - Scroll for Speed for Pet Equip. - Improves speed on Pet Equip. nSuccess rate:65%, speed+2
-2048007 - Scroll for Speed for Pet Equip. - Improves speed on Pet Equip. nSuccess rate:15%, speed+3
-2048008 - Scroll for jump for Pet Equip. - Improves jump on Pet equip. nSuccess rate:65%, jump+2
-2048009 - Scroll for jump for Pet Equip. - Improves jump on Pet equip. nSuccess rate:15%, jump+3
+2048006 - Scroll for Pet Equip. for Speed - Improves speed on Pet Equip. nSuccess rate:65%, speed+2
+2048007 - Scroll for Pet Equip. for Speed - Improves speed on Pet Equip. nSuccess rate:15%, speed+3
+2048008 - Scroll for Pet Equip. for Jump - Improves jump on Pet equip. nSuccess rate:65%, jump+2
+2048009 - Scroll for Pet Equip. for Jump - Improves jump on Pet equip. nSuccess rate:15%, jump+3
2048010 - Scroll for Pet Equip. for STR 60% - Improves strength on pet equipments.\nSuccess rate:60%, STR+1. The success rate of this scroll can be enhanced by Vega's Spell.
2048011 - Scroll for Pet Equip. for INT 60% - Improves intelligence on pet equipments.\nSuccess rate:60%, INT+1. The success rate of this scroll can be enhanced by Vega's Spell.
2048012 - Scroll for Pet Equip. for DEX 60% - Improves dexterity on pet equipments.\nSuccess rate:60%, DEX+1. The success rate of this scroll can be enhanced by Vega's Spell.
diff --git a/nbproject/private/private.properties b/nbproject/private/private.properties
index b7375e1e9d..67c9c27960 100644
--- a/nbproject/private/private.properties
+++ b/nbproject/private/private.properties
@@ -1,10 +1,6 @@
compile.on.save=true
do.depend=false
do.jar=true
-file.reference.mina-core-2.0.7.jar=C:\\Nexon\\HeavenMS\\cores\\mina-core-2.0.7.jar
-file.reference.mysql-connector-java-bin.jar=C:\\Nexon\\HeavenMS\\cores\\mysql-connector-java-bin.jar
-file.reference.slf4j-api-1.6.6.jar=C:\\Nexon\\HeavenMS\\cores\\slf4j-api-1.6.6.jar
-file.reference.slf4j-jdk14-1.7.5.jar=C:\\Nexon\\HeavenMS\\cores\\slf4j-jdk14-1.7.5.jar
javac.debug=true
javadoc.preview=true
user.properties.file=C:\\Users\\RonanLana\\AppData\\Roaming\\NetBeans\\8.0.2\\build.properties
diff --git a/nbproject/project.properties b/nbproject/project.properties
index cd7cebb20d..62e82d2465 100644
--- a/nbproject/project.properties
+++ b/nbproject/project.properties
@@ -30,20 +30,20 @@ endorsed.classpath=
excludes=
file.reference.HikariCP-java7-2.4.12.jar=cores/HikariCP-java7-2.4.12.jar
file.reference.MapleSolaxia-src=src
-file.reference.mina-core-2.0.7.jar=cores\\mina-core-2.0.7.jar
-file.reference.mysql-connector-java-bin.jar=cores\\mysql-connector-java-bin.jar
-file.reference.slf4j-api-1.6.6.jar=cores\\slf4j-api-1.6.6.jar
-file.reference.slf4j-jdk14-1.7.5.jar=cores\\slf4j-jdk14-1.7.5.jar
+file.reference.mina-core-2.0.7.jar=cores/mina-core-2.0.7.jar
+file.reference.mysql-connector-java-bin.jar=cores/mysql-connector-java-bin.jar
+file.reference.slf4j-api-1.6.6.jar=cores/slf4j-api-1.6.6.jar
+file.reference.slf4j-jdk14-1.7.5.jar=cores/slf4j-jdk14-1.7.5.jar
includes=**
jar.archive.disabled=${jnlp.enabled}
jar.compress=true
jar.index=${jnlp.enabled}
javac.classpath=\
+ ${file.reference.HikariCP-java7-2.4.12.jar}:\
${file.reference.mina-core-2.0.7.jar}:\
${file.reference.mysql-connector-java-bin.jar}:\
${file.reference.slf4j-api-1.6.6.jar}:\
- ${file.reference.slf4j-jdk14-1.7.5.jar}:\
- ${file.reference.HikariCP-java7-2.4.12.jar}
+ ${file.reference.slf4j-jdk14-1.7.5.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
diff --git a/scripts/event/3rdJob_bowman.js b/scripts/event/3rdJob_bowman.js
index 78db216aea..391023323a 100644
--- a/scripts/event/3rdJob_bowman.js
+++ b/scripts/event/3rdJob_bowman.js
@@ -47,7 +47,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 0);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/3rdJob_magician.js b/scripts/event/3rdJob_magician.js
index fa57fb4476..d9401415e0 100644
--- a/scripts/event/3rdJob_magician.js
+++ b/scripts/event/3rdJob_magician.js
@@ -47,7 +47,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 0);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/3rdJob_mount.js b/scripts/event/3rdJob_mount.js
index 0abd5af7cd..4b689f3596 100644
--- a/scripts/event/3rdJob_mount.js
+++ b/scripts/event/3rdJob_mount.js
@@ -62,7 +62,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 0);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/3rdJob_pirate.js b/scripts/event/3rdJob_pirate.js
index 3f1d8f7c65..e149acb930 100644
--- a/scripts/event/3rdJob_pirate.js
+++ b/scripts/event/3rdJob_pirate.js
@@ -47,7 +47,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 0);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/3rdJob_thief.js b/scripts/event/3rdJob_thief.js
index 774c87326a..8d925c9088 100644
--- a/scripts/event/3rdJob_thief.js
+++ b/scripts/event/3rdJob_thief.js
@@ -47,7 +47,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 0);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/3rdJob_warrior.js b/scripts/event/3rdJob_warrior.js
index e683c5f7b1..5c47d0916f 100644
--- a/scripts/event/3rdJob_warrior.js
+++ b/scripts/event/3rdJob_warrior.js
@@ -47,7 +47,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 0);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/4jberserk.js b/scripts/event/4jberserk.js
index 029c588ed7..1949dab5c2 100644
--- a/scripts/event/4jberserk.js
+++ b/scripts/event/4jberserk.js
@@ -58,7 +58,7 @@ function playerEntry(eim, player) {
player.changeMap(map, map.getPortal(0));
//TODO: hold time across map changes
-//player.getClient().getSession().write(tools.MaplePacketCreator.getClock(1800));
+//player.getClient().announce(tools.MaplePacketCreator.getClock(1800));
}
function playerDead(eim, player) {
diff --git a/scripts/event/Aran_2ndmount.js b/scripts/event/Aran_2ndmount.js
index fa2bfcd3d2..30cde9317d 100644
--- a/scripts/event/Aran_2ndmount.js
+++ b/scripts/event/Aran_2ndmount.js
@@ -53,7 +53,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 2);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/Aran_3rdmount.js b/scripts/event/Aran_3rdmount.js
index b85b44aef9..09ccfea5f8 100644
--- a/scripts/event/Aran_3rdmount.js
+++ b/scripts/event/Aran_3rdmount.js
@@ -54,7 +54,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 1);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/BalrogQuest.js b/scripts/event/BalrogQuest.js
index 67e9a3c6f8..797f7b23b2 100644
--- a/scripts/event/BalrogQuest.js
+++ b/scripts/event/BalrogQuest.js
@@ -29,7 +29,7 @@ var exitMap = 105100100;
var minMapId = 910520000;
var maxMapId = 910520000;
-var eventTime = 10; //10 minutes
+var eventTime = 10; //10 minutes
var lobbyRange = [0, 0];
@@ -56,7 +56,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 1);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/DollHouse.js b/scripts/event/DollHouse.js
index 9e8ac8f927..b307bbee25 100644
--- a/scripts/event/DollHouse.js
+++ b/scripts/event/DollHouse.js
@@ -26,7 +26,7 @@ importPackage(Packages.tools);
var entryMap = 922000010;
var exitMap = 221024400;
-var eventTime = 10; //10 minutes
+var eventTime = 10; //10 minutes
function init() {
em.setProperty("noEntry","false");
@@ -39,7 +39,7 @@ function playerEntry(eim, player) {
player.changeMap(entryMap, 0);
em.setProperty("noEntry","true");
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/GuildQuest.js b/scripts/event/GuildQuest.js
index de751d6f18..921efdf8f5 100644
--- a/scripts/event/GuildQuest.js
+++ b/scripts/event/GuildQuest.js
@@ -237,7 +237,7 @@ function changedMap(eim, player, mapid) {
function afterChangedMap(eim, player, mapid) {
if (mapid == 990000100) {
var texttt = "So, here is the brief. You guys should be warned that, once out on the fortress outskirts, anyone that would not be equipping the #b#t1032033##k will die instantly due to the deteriorated state of the air around there. That being said, once your team moves out, make sure to #bhit the glowing rocks#k in that region and #bequip the dropped item#k before advancing stages. That will protect you thoroughly from the air sickness. Good luck!";
- player.getClient().getSession().write(Packages.tools.MaplePacketCreator.getNPCTalk(9040000, /*(byte)*/ 0, texttt, "00 00", /*(byte)*/ 0));
+ player.getClient().announce(Packages.tools.MaplePacketCreator.getNPCTalk(9040000, /*(byte)*/ 0, texttt, "00 00", /*(byte)*/ 0));
}
}
diff --git a/scripts/event/Hak.js b/scripts/event/Hak.js
index f952751c0c..7a3e447418 100644
--- a/scripts/event/Hak.js
+++ b/scripts/event/Hak.js
@@ -34,7 +34,7 @@ function playerEntry(eim, player) {
onRide = eim.getMapFactory().getMap(birdRide[myRide]);
player.changeMap(onRide, onRide.getPortal(0));
- player.getClient().getSession().write(MaplePacketCreator.getClock(rideTime / 1000));
+ player.getClient().announce(MaplePacketCreator.getClock(rideTime / 1000));
eim.schedule("timeOut", rideTime);
}
diff --git a/scripts/event/HorntailBattle.js b/scripts/event/HorntailBattle.js
index 0be282d612..6ed3a6df0b 100644
--- a/scripts/event/HorntailBattle.js
+++ b/scripts/event/HorntailBattle.js
@@ -36,7 +36,7 @@ var clearMap = 240050600;
var minMapId = 240060000;
var maxMapId = 240060200;
-var eventTime = 15; // 15 minutes
+var eventTime = 120; // 120 minutes
var lobbyRange = [0, 0];
diff --git a/scripts/event/KerningTrain.js b/scripts/event/KerningTrain.js
index 916f812675..13c691df9e 100644
--- a/scripts/event/KerningTrain.js
+++ b/scripts/event/KerningTrain.js
@@ -35,8 +35,8 @@ function playerEntry(eim, player) {
onRide = eim.getMapFactory().getMap(trainRide[myRide]);
player.changeMap(onRide, onRide.getPortal(0));
- player.getClient().getSession().write(MaplePacketCreator.getClock(rideTime / 1000));
- player.getClient().getSession().write(MaplePacketCreator.earnTitleMessage("The next stop is at Kerning " + (myRide == 0 ? "Square" : "Subway") + " Station. The exit is to your left."));
+ player.getClient().announce(MaplePacketCreator.getClock(rideTime / 1000));
+ player.getClient().announce(MaplePacketCreator.earnTitleMessage("The next stop is at Kerning " + (myRide == 0 ? "Square" : "Subway") + " Station. The exit is to your left."));
eim.schedule("timeOut", rideTime);
}
diff --git a/scripts/event/MK_PrimeMinister2.js b/scripts/event/MK_PrimeMinister2.js
index ea9e3c1009..c1daee1b21 100644
--- a/scripts/event/MK_PrimeMinister2.js
+++ b/scripts/event/MK_PrimeMinister2.js
@@ -2,7 +2,7 @@ importPackage(Packages.tools);
importPackage(Packages.server.life);
var minPlayers = 1;
-var eventTime = 10;
+var eventTime = 10; // 10 minutes
var entryMap = 106021402;
var exitMap = 106021600;
@@ -36,7 +36,7 @@ function playerEntry(eim, player){
var pm = MapleLifeFactory.getMonster(3300008);
weddinghall.spawnMonsterOnGroundBelow(pm, new Packages.java.awt.Point(472, 27));
- player.getClient().getSession().write(MaplePacketCreator.getClock(eventTime * 60));
+ player.getClient().announce(MaplePacketCreator.getClock(eventTime * 60));
eim.startEventTimer(eventTime * 60000);
}
diff --git a/scripts/event/OrbisPQ.js b/scripts/event/OrbisPQ.js
index c7bce48ab7..9ab4462fdd 100644
--- a/scripts/event/OrbisPQ.js
+++ b/scripts/event/OrbisPQ.js
@@ -180,7 +180,7 @@ function playerEntry(eim, player) {
player.changeMap(map, map.getPortal(0));
var texttt = "Hi, my name is Eak, the Chamberlain of the Goddess. Don't be alarmed; you won't be able to see me right now. Back when the Goddess turned into a block of stone, I simultaneously lost my own power. If you gather up the power of the Magic Cloud of Orbis, however, then I'll be able to recover my body and re-transform back to my original self. Please collect #b20#k Magic Clouds and bring them back to me. Right now, you'll only see me as a tiny, flickering light.";
- player.getClient().getSession().write(Packages.tools.MaplePacketCreator.getNPCTalk(2013001, /*(byte)*/ 0, texttt, "00 00", /*(byte)*/ 0));
+ player.getClient().announce(Packages.tools.MaplePacketCreator.getNPCTalk(2013001, /*(byte)*/ 0, texttt, "00 00", /*(byte)*/ 0));
}
function scheduledTimeout(eim) {
diff --git a/scripts/event/PinkBeanBattle.js b/scripts/event/PinkBeanBattle.js
index e4ee83165d..a147b862ff 100644
--- a/scripts/event/PinkBeanBattle.js
+++ b/scripts/event/PinkBeanBattle.js
@@ -37,7 +37,7 @@ var clearMap = 270050300;
var minMapId = 270050100;
var maxMapId = 270050300;
-var eventTime = 100; // 100 minutes
+var eventTime = 140; // 140 minutes
var lobbyRange = [0, 0];
diff --git a/scripts/event/RockSpirit.js b/scripts/event/RockSpirit.js
index b1cbdd4662..c8b00228d8 100644
--- a/scripts/event/RockSpirit.js
+++ b/scripts/event/RockSpirit.js
@@ -22,7 +22,7 @@
importPackage(Packages.tools);
var exitMap;
-var startMap;
+var entryMap;
var otherMap;
var minPlayers = 1;
var fightTime = 60;
@@ -30,7 +30,7 @@ var timer = 1000 * 60 * fightTime;
function init() {
exitMap = em.getChannelServer().getMapFactory().getMap(103040400);
- startMap = em.getChannelServer().getMapFactory().getMap(103040410);
+ entryMap = em.getChannelServer().getMapFactory().getMap(103040410);
otherMap = em.getChannelServer().getMapFactory().getMap(103040420);
}
@@ -44,7 +44,7 @@ function setup() {
function afterSetup(eim) {}
function respawn(eim) {
- var map = eim.getMapInstance(startMap.getId());
+ var map = eim.getMapInstance(entryMap.getId());
var map2 = eim.getMapInstance(otherMap.getId());
map.allowSummonState(true);
map2.allowSummonState(true);
@@ -55,7 +55,7 @@ function respawn(eim) {
function playerEntry(eim, player) {
- var amplifierMap = eim.getMapInstance(startMap.getId());
+ var amplifierMap = eim.getMapInstance(entryMap.getId());
player.changeMap(amplifierMap);
eim.schedule("timeOut", timer);
}
@@ -107,7 +107,7 @@ function playerExit(eim, player) {
function moveMap(eim, player) {
if (player.getMap().getId() == exitMap.getId()) {
removePlayer(eim, player);
- player.getClient().getSession().write(MaplePacketCreator.removeClock());
+ player.getClient().announce(MaplePacketCreator.removeClock());
eim.dispose();
}
}
diff --git a/scripts/event/RockSpiritVIP.js b/scripts/event/RockSpiritVIP.js
index 4895d5dc2b..f7e60a5e6b 100644
--- a/scripts/event/RockSpiritVIP.js
+++ b/scripts/event/RockSpiritVIP.js
@@ -22,7 +22,7 @@
importPackage(Packages.tools);
var exitMap;
-var startMap;
+var entryMap;
var otherMap;
var minPlayers = 1;
var fightTime = 30;
@@ -30,7 +30,7 @@ var timer = 1000 * 60 * fightTime;
function init() {
exitMap = em.getChannelServer().getMapFactory().getMap(103040400);
- startMap = em.getChannelServer().getMapFactory().getMap(103040440);
+ entryMap = em.getChannelServer().getMapFactory().getMap(103040440);
otherMap = em.getChannelServer().getMapFactory().getMap(103040450);
}
@@ -44,7 +44,7 @@ function setup() {
function afterSetup(eim) {}
function respawn(eim) {
- var map = eim.getMapInstance(startMap.getId());
+ var map = eim.getMapInstance(entryMap.getId());
var map2 = eim.getMapInstance(otherMap.getId());
map.allowSummonState(true);
map2.allowSummonState(true);
@@ -55,7 +55,7 @@ function respawn(eim) {
function playerEntry(eim, player) {
- var amplifierMap = eim.getMapInstance(startMap.getId());
+ var amplifierMap = eim.getMapInstance(entryMap.getId());
player.changeMap(amplifierMap);
eim.schedule("timeOut", timer);
}
@@ -107,7 +107,7 @@ function playerExit(eim, player) {
function moveMap(eim, player) {
if (player.getMap().getId() == exitMap.getId()) {
removePlayer(eim, player);
- player.getClient().getSession().write(MaplePacketCreator.removeClock());
+ player.getClient().announce(MaplePacketCreator.removeClock());
eim.dispose();
}
}
diff --git a/scripts/event/ZakumBattle.js b/scripts/event/ZakumBattle.js
index 7fc014adc3..a91296c187 100644
--- a/scripts/event/ZakumBattle.js
+++ b/scripts/event/ZakumBattle.js
@@ -36,7 +36,7 @@ var clearMap = 211042400;
var minMapId = 280030000;
var maxMapId = 280030000;
-var eventTime = 60; // 60 minutes
+var eventTime = 120; // 120 minutes
var lobbyRange = [0, 0];
diff --git a/scripts/map/onFirstUserEnter/killing_MapSetting.js b/scripts/map/onFirstUserEnter/killing_MapSetting.js
index c45b9865ec..8b0ccf4333 100644
--- a/scripts/map/onFirstUserEnter/killing_MapSetting.js
+++ b/scripts/map/onFirstUserEnter/killing_MapSetting.js
@@ -3,7 +3,7 @@
function start(ms) {
//var pq = ms.getPyramid();
//ms.getPlayer().resetEnteredScript();
- //ms.getClient().getSession().write(MaplePacketCreator.getClock(pq.timer()));
+ //ms.getClient().announce(MaplePacketCreator.getClock(pq.timer()));
}
/*
killing/first/stage
diff --git a/scripts/map/onFirstUserEnter/spaceGaGa_start.js b/scripts/map/onFirstUserEnter/spaceGaGa_start.js
index ce0beaf351..57d476e830 100644
--- a/scripts/map/onFirstUserEnter/spaceGaGa_start.js
+++ b/scripts/map/onFirstUserEnter/spaceGaGa_start.js
@@ -29,11 +29,11 @@ var player;
function start(ms) {
player = ms.getPlayer();
player.resetEnteredScript();
- ms.getClient().getSession().write(MaplePacketCreator.showEffect("event/space/start"));
+ ms.getClient().announce(MaplePacketCreator.showEffect("event/space/start"));
player.startMapEffect("Please rescue Gaga within the time limit.", 5120027);
var map = player.getMap();
if (map.getTimeLeft() > 0) {
- ms.getClient().getSession().write(MaplePacketCreator.getClock(map.getTimeLeft()));
+ ms.getClient().announce(MaplePacketCreator.getClock(map.getTimeLeft()));
} else {
map.addMapTimer(180);
}
diff --git a/scripts/npc/1061010.js b/scripts/npc/1061010.js
index dd640d2da1..7835599d79 100644
--- a/scripts/npc/1061010.js
+++ b/scripts/npc/1061010.js
@@ -21,13 +21,14 @@ function action(mode, type, selection) {
if(status == 0){
cm.sendYesNo("Would you like to leave?");
}else if(status == 1){
- var mapid = cm.getMapId();
- if(mapid == 108010101) cm.getPlayer().changeMap(105040305);
- else if(mapid == 108010201) cm.getPlayer().changeMap(100040106);
- else if(mapid == 108010301) cm.getPlayer().changeMap(105070001);
- else if(mapid == 108010401) cm.getPlayer().changeMap(107000402);
- else if(mapid == 108010501) cm.getPlayer().changeMap(105070200);
+ var mapid = cm.getMapId(), exitid = mapid;
+ if(mapid == 108010101) exitid = 105040305;
+ else if(mapid == 108010201) exitid = 100040106;
+ else if(mapid == 108010301) exitid = 105070001;
+ else if(mapid == 108010401) exitid = 107000402;
+ else if(mapid == 108010501) exitid = 105070200;
+ if (mapid != exitid) cm.getPlayer().changeMap(exitid);
cm.dispose();
}
}
diff --git a/scripts/npc/2020008.js b/scripts/npc/2020008.js
index 79679a6727..2ce3f079ad 100644
--- a/scripts/npc/2020008.js
+++ b/scripts/npc/2020008.js
@@ -92,7 +92,7 @@ function action(mode, type, selection){
else if (status == 2) {
if (cm.getPlayer().getRemainingSp() > 0)
if (cm.getPlayer().getRemainingSp() > (cm.getLevel() - 70) * 3) {
- cm.sendNext("Please, use all your SP before contining.");
+ cm.sendNext("Please, use all your SP before continuing.");
cm.dispose();
return;
}
diff --git a/scripts/npc/2020009.js b/scripts/npc/2020009.js
index 0ed7c47477..39ab767c6c 100644
--- a/scripts/npc/2020009.js
+++ b/scripts/npc/2020009.js
@@ -64,7 +64,7 @@ function action(mode, type, selection){
else if (status == 2) {
if (cm.getPlayer().getRemainingSp() > 0)
if (cm.getPlayer().getRemainingSp() > (cm.getLevel() - 70) * 3) {
- cm.sendNext("Please, use all your SP before contining.");
+ cm.sendNext("Please, use all your SP before continuing.");
cm.dispose();
return;
}
diff --git a/scripts/npc/2020010.js b/scripts/npc/2020010.js
index 8cadcdca12..cab227ff6f 100644
--- a/scripts/npc/2020010.js
+++ b/scripts/npc/2020010.js
@@ -66,7 +66,7 @@ function action(mode, type, selection){
else if (status == 2) {
if (cm.getPlayer().getRemainingSp() > 0)
if (cm.getPlayer().getRemainingSp() > (cm.getLevel() - 70) * 3) {
- cm.sendNext("Please, use all your SP before contining.");
+ cm.sendNext("Please, use all your SP before continuing.");
cm.dispose();
return;
}
diff --git a/scripts/npc/2020011.js b/scripts/npc/2020011.js
index 980ec018d3..a85730f09d 100644
--- a/scripts/npc/2020011.js
+++ b/scripts/npc/2020011.js
@@ -64,7 +64,7 @@ function action(mode, type, selection){
else if (status == 2) {
if (cm.getPlayer().getRemainingSp() > 0)
if (cm.getPlayer().getRemainingSp() > (cm.getLevel() - 70) * 3) {
- cm.sendNext("Please, use all your SP before contining.");
+ cm.sendNext("Please, use all your SP before continuing.");
cm.dispose();
return;
}
diff --git a/scripts/npc/2020013.js b/scripts/npc/2020013.js
index d8c6615f57..edec8efb1a 100644
--- a/scripts/npc/2020013.js
+++ b/scripts/npc/2020013.js
@@ -64,7 +64,7 @@ function action(mode, type, selection){
else if (status == 2) {
if (cm.getPlayer().getRemainingSp() > 0)
if (cm.getPlayer().getRemainingSp() > (cm.getLevel() - 70) * 3) {
- cm.sendNext("Please, use all your SP before contining.");
+ cm.sendNext("Please, use all your SP before continuing.");
cm.dispose();
return;
}
diff --git a/scripts/npc/2030006.js b/scripts/npc/2030006.js
index 3cc2d17415..6cc117e1e1 100644
--- a/scripts/npc/2030006.js
+++ b/scripts/npc/2030006.js
@@ -39,7 +39,7 @@ var questionTree = [
//Questions Related to MONSTERS
["Green Mushroom, Tree Stump, Bubbling, Axe Stump, Octopus, which is highest level of all?", ["Tree Stump", "Bubbling", "Axe Stump", "Octopus", "Green Mushroom"], 2],
["Which monster will be seen during the ship trip to Orbis/Ellinia?", ["Werewolf", "Slime", "Crimson Balrog", "Zakum", "Star Pixie"], 2],
- ["Maple Island doesn't have which following monsters?", ["Shroom", "Blue Snail", "Orange Mushroom", "Red Snail", "Pig"], 4],
+ ["Maple Island doesn't have which following monsters?", ["Green Mushroom", "Blue Snail", "Orange Mushroom", "Red Snail", "Pig"], 0],
["Which monster is not at Victoria Island and Sleepywood?", ["Evil Eye", "Sentinel", "Jr. Balrog", "Ghost Stump", "Snail"], 1],
["El Nath doesn't have which following monsters?", ["Dark Yeti", "Dark Ligator", "Yeti & Pepe", "Bain", "Coolie Zombie"], 1],
["Which of following monsters can fly?", ["Malady", "Ligator", "Cold Eye", "Meerkat", "Alishar"], 0],
@@ -80,6 +80,8 @@ var question;
var questionPool;
var questionPoolCursor;
+var questionAnswer;
+
function start() {
status = -1;
action(1, 0, 0);
@@ -121,7 +123,10 @@ function action(mode, type, selection) {
question = fetchNextQuestion();
var questionHead = generateQuestionHeading();
var questionEntry = questionTree[question][0];
- var questionOptions = generateSelectionMenu(questionTree[question][1]);
+
+ var questionData = generateSelectionMenu(questionTree[question][1], questionTree[question][2]);
+ var questionOptions = questionData[0];
+ questionAnswer = questionData[1];
cm.sendSimple(questionHead + questionEntry + "\r\n\r\n#b" + questionOptions + "#k");
} else if(status >= 2 && status <= 5) {
@@ -134,7 +139,10 @@ function action(mode, type, selection) {
question = fetchNextQuestion();
var questionHead = generateQuestionHeading();
var questionEntry = questionTree[question][0];
- var questionOptions = generateSelectionMenu(questionTree[question][1]);
+
+ var questionData = generateSelectionMenu(questionTree[question][1], questionTree[question][2]);
+ var questionOptions = questionData[0];
+ questionAnswer = questionData[1];
cm.sendSimple(questionHead + questionEntry + "\r\n\r\n#b" + questionOptions + "#k");
} else if(status == 6) {
@@ -155,7 +163,7 @@ function action(mode, type, selection) {
}
function evaluateAnswer(selection) {
- return selection == questionTree[question][2];
+ return selection == questionAnswer;
}
function generateQuestionHeading() {
@@ -189,10 +197,36 @@ function fetchNextQuestion() {
return next;
}
-function generateSelectionMenu(array) {
+function shuffle(array) {
+ var currentIndex = array.length, temporaryValue, randomIndex;
+
+ // While there remain elements to shuffle...
+ while (0 !== currentIndex) {
+
+ // Pick a remaining element...
+ randomIndex = Math.floor(Math.random() * currentIndex);
+ currentIndex -= 1;
+
+ // And swap it with the current element.
+ temporaryValue = array[currentIndex];
+ array[currentIndex] = array[randomIndex];
+ array[randomIndex] = temporaryValue;
+ }
+
+ return array;
+}
+
+function generateSelectionMenu(array, answer) {
+ var answerStr = array[answer], answerPos = -1;
+
+ shuffle(array);
+
var menu = "";
for (var i = 0; i < array.length; i++) {
menu += "#L" + i + "#" + array[i] + "#l\r\n";
+ if (answerStr == array[i]) {
+ answerPos = i;
+ }
}
- return menu;
+ return [menu, answerPos];
}
\ No newline at end of file
diff --git a/scripts/npc/2030008.js b/scripts/npc/2030008.js
index 927124bcc8..31a3afa6f4 100644
--- a/scripts/npc/2030008.js
+++ b/scripts/npc/2030008.js
@@ -107,14 +107,14 @@ function action(mode, type, selection) {
} else {
if(cm.haveItem(4031061) && cm.haveItem(4031062)) {
if(!cm.haveItem(4000082, 30)) {
- cm.sendOk("You have completed the trials, however there's still the need of #b30 #t4000082##k to forge the #t4001017#.");
+ cm.sendOk("You have completed the trials, however there's still the need of #b30 #t4000082##k to forge 5 #t4001017#.");
} else {
cm.completeQuest(100201);
cm.gainItem(4031061, -1);
cm.gainItem(4031062, -1);
cm.gainItem(4000082, -30);
- cm.gainItem(4001017, 1);
+ cm.gainItem(4001017, 5);
cm.sendNext("You #rhave completed the trials#k, from now on having my approval to challenge Zakum.");
}
diff --git a/scripts/npc/9201021.js b/scripts/npc/9201021.js
index 2198fcd7ab..49790a5384 100644
--- a/scripts/npc/9201021.js
+++ b/scripts/npc/9201021.js
@@ -21,7 +21,7 @@
*/
var status = 0;
-function start() {
+function start() {
if(cm.getMapId() != 680000401) cm.sendSimple("Hello, where would you like to go?\r\n#b" + ((cm.getMapId() != 680000400) ? "#L0#Untamed Hearts Hunting Ground#l\r\n" : "") + ((cm.getMapId() == 680000400) ? "#L1#I have 7 keys. Bring me to smash boxes#l\r\n" : "") + "#L2#Please warp me out.#l#k");
else cm.sendSimple("Hello, do you want to go back now? Returning here again will cost you #rother 7 keys#k.\r\n#b#L2#Please warp me back to the training grounds.#l#k");
}
diff --git a/scripts/npc/9977777.js b/scripts/npc/9977777.js
index eecf0196a7..49f1748f54 100644
--- a/scripts/npc/9977777.js
+++ b/scripts/npc/9977777.js
@@ -82,19 +82,22 @@ function writeFeatureTab_PlayerSocialNetwork() {
addFeature("Automated support for Player NPCs and Hall of Fame.");
addFeature("Engagement & Wedding system.");
addFeature("Equipments displays to everyone it's level & EXP info.");
+ addFeature("Further improved the existent minigame mechanics.");
}
function writeFeatureTab_CashItems() {
addFeature("EXP/DROP/Cosmetic Coupons.");
addFeature("EXP/DROP Coupon as buff effect during active time.");
addFeature("Great deal of cash items functional.");
+ addFeature("Code coupons functional, with multi-items support.");
+ addFeature("Merged unique ids for pets, rings and cash items.");
addFeature("MapleTV mechanics stabilized and split by world.");
addFeature("GMS-esque omok/match card drop chances.");
addFeature("New town scroll: antibanish. Counters boss banishes.");
addFeature("Inventory system checks for free slot & stack space.");
addFeature("Storage with 'Arrange Items' feature functional.");
addFeature("Close-quarters evaluation mode for items.");
- addFeature("Further improved Karma scissors.");
+ addFeature("Reviewed Karma scissors & Untradeable items.");
addFeature("Scroll for Spikes on Shoes.");
addFeature("Scroll for Cold Protection.");
addFeature("Vega's spell.");
@@ -133,6 +136,8 @@ function writeFeatureTab_MonstersMapsReactors() {
addFeature("Updated scripted portals, now with proper portal SFX.");
addFeature("Reviewed Masteria, W. Tour, N. Desert and Neo City.");
addFeature("Added world maps for M. Castle, W. Tour & Ellin areas.");
+ addFeature("Added W. Tour & Masteria continents in the world map.");
+ addFeature("Reviewed several issues with W. Map tooltips & links.");
addFeature("Giant Cake boss drops s. bags and Maple items.");
}
@@ -197,6 +202,7 @@ function writeFeatureTab_Commands() {
addFeature("Rank command highlighting users by world or overall.");
addFeature("Server commands layered by GM levels.");
addFeature("Revamped command files layout - thanks Arthur L!");
+ addFeature("Improved 'Search' performance & added map search.");
}
function writeFeatureTab_CustomNPCs() {
@@ -274,9 +280,9 @@ function action(mode, type, selection) {
status++;
else
status--;
-
+
if (status == 0) {
- var sendStr = "HeavenMS was developed on the timespan of 3 years, based on where Solaxia left. On the meantime many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features from #bHeavenMS#k:\r\n\r\n";
+ var sendStr = "HeavenMS was developed on the timespan of 3 years, based on where Solaxia left. I'm glad to say the development itself had continuously been agraciated by dozens of contributors and cheerers (truly thanks for the trusting vow, guys & gals!).\r\n\r\nTalking about results: many nice features emerged, development aimed to get back the old GMS experience. Now many of these so-long missing features are gracefully presented to you in the shape of this server. Long live MapleStory!!\r\n\r\nThese are the features from #bHeavenMS#k:\r\n\r\n";
for(var i = 0; i < tabs.length; i++) {
sendStr += "#L" + i + "##b" + tabs[i] + "#k#l\r\n";
}
diff --git a/scripts/npc/credits.js b/scripts/npc/credits.js
index 11d59bf777..7544ede3f7 100644
--- a/scripts/npc/credits.js
+++ b/scripts/npc/credits.js
@@ -37,6 +37,7 @@ function writeServerStaff_HeavenMS() {
addPerson("Ronan", "Developer");
addPerson("Vcoc", "Freelance Developer");
addPerson("Thora", "Contributor");
+ addPerson("GabrielSin", "Contributor");
setHistory(2015, 2018);
}
diff --git a/sql/db_database.sql b/sql/db_database.sql
index 589c9cf177..05a51d72d9 100644
--- a/sql/db_database.sql
+++ b/sql/db_database.sql
@@ -16,7 +16,6 @@ CREATE TABLE IF NOT EXISTS `accounts` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(13) NOT NULL DEFAULT '',
`password` varchar(128) NOT NULL DEFAULT '',
- `salt` varchar(128) DEFAULT NULL,
`pin` varchar(10) DEFAULT NULL,
`pic` varchar(26) DEFAULT NULL,
`loggedin` tinyint(4) NOT NULL DEFAULT '0',
@@ -4517,7 +4516,7 @@ INSERT IGNORE INTO `temp_data` (`id`, `dropperid`, `itemid`, `minimum_quantity`,
(4298, 5130107, 4130006, 1, 1, 0, 6000),
(4299, 5130107, 4130011, 1, 1, 0, 6000),
(4300, 5130108, 4000069, 1, 1, 0, 600000),
-(4301, 5130108, 4000082, 1, 1, 0, 40000),
+(4301, 5130108, 4000082, 1, 1, 0, 20000),
(4302, 5130108, 2000003, 1, 1, 0, 20000),
(4303, 5130108, 2000004, 1, 1, 0, 20000),
(4304, 5130108, 2000002, 1, 1, 0, 20000),
@@ -12926,15 +12925,6 @@ CREATE TABLE IF NOT EXISTS `hiredmerchant` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
-CREATE TABLE IF NOT EXISTS `htsquads` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `channel` int(10) unsigned NOT NULL,
- `leaderid` int(10) unsigned NOT NULL DEFAULT '0',
- `status` int(10) unsigned NOT NULL DEFAULT '0',
- `members` int(10) unsigned NOT NULL DEFAULT '0',
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
-
CREATE TABLE IF NOT EXISTS `hwidaccounts` (
`accountid` int(11) NOT NULL DEFAULT '0',
`hwid` varchar(40) NOT NULL DEFAULT '',
@@ -16393,13 +16383,21 @@ CREATE TABLE IF NOT EXISTS `notes` (
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `nxcode` (
- `code` varchar(15) NOT NULL,
- `valid` int(11) NOT NULL DEFAULT '1',
- `user` varchar(13) DEFAULT NULL,
- `type` int(11) NOT NULL DEFAULT '0',
- `item` int(11) NOT NULL DEFAULT '10000',
- PRIMARY KEY (`code`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1;
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `code` varchar(17) NOT NULL UNIQUE,
+ `retriever` varchar(13) DEFAULT NULL,
+ `expiration` bigint(20) unsigned NOT NULL DEFAULT '0',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
+
+CREATE TABLE IF NOT EXISTS `nxcode_items` (
+ `id` int(11) NOT NULL AUTO_INCREMENT,
+ `codeid` int(11) NOT NULL,
+ `type` int(11) NOT NULL DEFAULT '5',
+ `item` int(11) NOT NULL DEFAULT '4000000',
+ `quantity` int(11) NOT NULL DEFAULT '1',
+ PRIMARY KEY (`id`)
+) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
CREATE TABLE IF NOT EXISTS `nxcoupons` (
`id` int(11) NOT NULL AUTO_INCREMENT,
@@ -21414,15 +21412,6 @@ CREATE TABLE IF NOT EXISTS `wishlists` (
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
-CREATE TABLE IF NOT EXISTS `zaksquads` (
- `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
- `channel` int(10) unsigned NOT NULL,
- `leaderid` int(10) unsigned NOT NULL DEFAULT '0',
- `status` int(10) unsigned NOT NULL DEFAULT '0',
- `members` int(10) unsigned NOT NULL DEFAULT '0',
- PRIMARY KEY (`id`)
-) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
-
ALTER TABLE `dueyitems`
ADD CONSTRAINT `dueyitems_ibfk_1` FOREIGN KEY (`PackageId`) REFERENCES `dueypackages` (`PackageId`) ON DELETE CASCADE;
diff --git a/sql/db_drops.sql b/sql/db_drops.sql
index f46dbf5aee..9bbc61ad1a 100644
--- a/sql/db_drops.sql
+++ b/sql/db_drops.sql
@@ -412,9 +412,9 @@ USE `heavenms`;
(1210102, 4030001, 1, 1, 0, 10000),
(9300229, 4030001, 1, 1, 0, 10000),
(9500102, 4030001, 1, 1, 0, 10000),
-(1210102, 4001345, 1, 1, 0, 7000),
-(9300229, 4001345, 1, 1, 0, 7000),
-(9500102, 4001345, 1, 1, 0, 7000),
+(1210102, 4001345, 1, 1, 0, 70000),
+(9300229, 4001345, 1, 1, 0, 70000),
+(9500102, 4001345, 1, 1, 0, 70000),
(1210102, 2000000, 1, 1, 0, 40000),
(9300229, 2000000, 1, 1, 0, 40000),
(9500102, 2000000, 1, 1, 0, 40000),
@@ -508,18 +508,18 @@ USE `heavenms`;
(1210101, 4000021, 1, 1, 0, 200000),
(9300059, 4000021, 1, 1, 0, 200000),
(9400242, 4000021, 1, 1, 0, 200000),
-(1210101, 4003004, 1, 1, 0, 7000),
-(9300059, 4003004, 1, 1, 0, 7000),
-(9400242, 4003004, 1, 1, 0, 7000),
+(1210101, 4003004, 1, 1, 0, 70000),
+(9300059, 4003004, 1, 1, 0, 70000),
+(9400242, 4003004, 1, 1, 0, 70000),
(1210101, 4030012, 1, 1, 0, 125000),
(9300059, 4030012, 1, 1, 0, 125000),
(9400242, 4030012, 1, 1, 0, 125000),
-(1210101, 4001343, 1, 1, 0, 7000),
-(9300059, 4001343, 1, 1, 0, 7000),
-(9400242, 4001343, 1, 1, 0, 7000),
-(1210101, 4001342, 1, 1, 0, 7000),
-(9300059, 4001342, 1, 1, 0, 7000),
-(9400242, 4001342, 1, 1, 0, 7000),
+(1210101, 4001343, 1, 1, 0, 70000),
+(9300059, 4001343, 1, 1, 0, 70000),
+(9400242, 4001343, 1, 1, 0, 70000),
+(1210101, 4001342, 1, 1, 0, 70000),
+(9300059, 4001342, 1, 1, 0, 70000),
+(9400242, 4001342, 1, 1, 0, 70000),
(1210101, 2000000, 1, 1, 0, 40000),
(9300059, 2000000, 1, 1, 0, 40000),
(9400242, 2000000, 1, 1, 0, 40000),
@@ -566,9 +566,9 @@ USE `heavenms`;
(9300059, 1072285, 1, 1, 0, 700),
(9400242, 1072285, 1, 1, 0, 700),
(1110101, 4030009, 1, 1, 0, 28000),
-(1110101, 4001344, 1, 1, 0, 7000),
-(1110101, 4001356, 1, 1, 0, 7000),
-(1110101, 4001360, 1, 1, 0, 12000),
+(1110101, 4001344, 1, 1, 0, 70000),
+(1110101, 4001356, 1, 1, 0, 70000),
+(1110101, 4001360, 1, 1, 0, 120000),
(1110101, 2000000, 1, 1, 0, 40000),
(1110101, 2040802, 1, 1, 0, 750),
(1110101, 2380008, 1, 1, 0, 0),
@@ -4654,7 +4654,7 @@ USE `heavenms`;
(9300186, 1082195, 1, 1, 0, 40000),
(9500339, 1082195, 1, 1, 0, 40000),
(9303003, 1082195, 1, 1, 0, 700),
-(3300008, 4001318, 1, 1, 0, 7000),
+(3300008, 4001318, 1, 1, 0, 600000),
(3300008, 2388070, 1, 4, 0, 20000),
(3300008, 2000002, 1, 4, 0, 40000),
(3300008, 2000003, 1, 4, 0, 40000),
@@ -16180,8 +16180,8 @@ USE `heavenms`;
(9420513, 1060106, 1, 1, 0, 40000),
(9420513, 1072173, 1, 1, 0, 40000),
(9420513, 1332026, 1, 1, 0, 40000),
-(9400549, 4031903, 1, 1, 0, 7000),
-(9400571, 4031903, 1, 1, 0, 7000),
+(9400549, 4031903, 1, 1, 0, 100000),
+(9400571, 4031903, 1, 1, 0, 100000),
(9400549, 2020014, 1, 4, 0, 3000),
(9400571, 2020014, 1, 4, 0, 3000),
(9400549, 2002025, 1, 4, 0, 10000),
@@ -20007,29 +20007,29 @@ USE `heavenms`;
(9300154, 4031782, 1, 1, 0, 400000),
(9300154, 4031783, 1, 1, 0, 400000),
(9300154, 4031784, 1, 1, 0, 400000),
-(1110100, 4032317, 1, 1, 21717, 20000),
-(1110130, 4032317, 1, 1, 21717, 20000),
-(1110100, 4032318, 1, 1, 21718, 20000),
-(1110130, 4032318, 1, 1, 21718, 20000),
+(1110100, 4032317, 1, 1, 21717, 40000),
+(1110130, 4032317, 1, 1, 21717, 40000),
+(1110100, 4032318, 1, 1, 21718, 40000),
+(1110130, 4032318, 1, 1, 21718, 40000),
(9400204, 4130020, 1, 1, 0, 3000),
-(9400614, 4001341, 1, 1, 28172, 15000),
-(9400615, 4001341, 1, 1, 28172, 15000),
-(9400616, 4001341, 1, 1, 28172, 15000),
-(1110101, 4001347, 1, 1, 28229, 80000),
-(1110100, 4001348, 1, 1, 28231, 400000),
-(1120100, 4001349, 1, 1, 28235, 100000),
-(1210101, 4001350, 1, 1, 28235, 100000),
-(2220100, 4001351, 1, 1, 28237, 100000),
-(9400614, 4001352, 1, 1, 28205, 40000),
-(9400615, 4001352, 1, 1, 28205, 40000),
-(9400616, 4001352, 1, 1, 28205, 40000),
-(9400617, 4001352, 1, 1, 28205, 40000),
-(9400617, 4001362, 1, 1, 28252, 40000),
-(9400655, 4001362, 1, 1, 28252, 40000),
-(9400656, 4001362, 1, 1, 28252, 40000),
-(9400617, 4001363, 1, 1, 28252, 40000),
-(9400655, 4001363, 1, 1, 28252, 40000),
-(9400656, 4001363, 1, 1, 28252, 40000),
+(9400614, 4001341, 1, 1, 28172, 30000),
+(9400615, 4001341, 1, 1, 28172, 30000),
+(9400616, 4001341, 1, 1, 28172, 30000),
+(1110101, 4001347, 1, 1, 28229, 160000),
+(1110100, 4001348, 1, 1, 28231, 800000),
+(1120100, 4001349, 1, 1, 28235, 200000),
+(1210101, 4001350, 1, 1, 28235, 200000),
+(2220100, 4001351, 1, 1, 28237, 200000),
+(9400614, 4001352, 1, 1, 28205, 80000),
+(9400615, 4001352, 1, 1, 28205, 80000),
+(9400616, 4001352, 1, 1, 28205, 80000),
+(9400617, 4001352, 1, 1, 28205, 80000),
+(9400617, 4001362, 1, 1, 28252, 80000),
+(9400655, 4001362, 1, 1, 28252, 80000),
+(9400656, 4001362, 1, 1, 28252, 80000),
+(9400617, 4001363, 1, 1, 28252, 80000),
+(9400655, 4001363, 1, 1, 28252, 80000),
+(9400656, 4001363, 1, 1, 28252, 80000),
(9400609, 2000002, 1, 1, 0, 40000),
(9400609, 2000003, 1, 1, 0, 40000),
(9400609, 2000004, 1, 1, 0, 10000),
@@ -20077,17 +20077,17 @@ USE `heavenms`;
(9400613, 1082258, 1, 1, 0, 5000),
(9400613, 1072421, 1, 1, 0, 5000),
(4110302, 2383007, 1, 1, 0, 8000),
-(130100, 4032374, 1, 1, 2405, 40000),
-(1110101, 4032374, 1, 1, 2405, 40000),
-(210100, 4032376, 1, 1, 2406, 40000),
-(1110101, 4032376, 1, 1, 2406, 40000),
-(210100, 4032377, 1, 1, 2407, 40000),
-(1210101, 4032377, 1, 1, 2407, 40000),
-(130100, 4032378, 1, 1, 2408, 40000),
-(1120100, 4032378, 1, 1, 2408, 40000),
-(1110100, 4032379, 1, 1, 2409, 40000),
-(1210100, 4032379, 1, 1, 2409, 40000),
-(2130100, 4001344, 1, 1, 0, 7000),
+(130100, 4032374, 1, 1, 2405, 80000),
+(1110101, 4032374, 1, 1, 2405, 80000),
+(210100, 4032376, 1, 1, 2406, 80000),
+(1110101, 4032376, 1, 1, 2406, 80000),
+(210100, 4032377, 1, 1, 2407, 80000),
+(1210101, 4032377, 1, 1, 2407, 80000),
+(130100, 4032378, 1, 1, 2408, 80000),
+(1120100, 4032378, 1, 1, 2408, 80000),
+(1110100, 4032379, 1, 1, 2409, 80000),
+(1210100, 4032379, 1, 1, 2409, 80000),
+(2130100, 4001344, 1, 1, 0, 70000),
(7220002, 4031789, 1, 1, 3844, 999999),
(8830007, 1302112, 1, 1, 0, 4000),
(8830007, 1302113, 1, 1, 0, 4000),
diff --git a/src/client/AbstractMapleCharacterObject.java b/src/client/AbstractMapleCharacterObject.java
index a7a422391a..e1c999c743 100644
--- a/src/client/AbstractMapleCharacterObject.java
+++ b/src/client/AbstractMapleCharacterObject.java
@@ -42,6 +42,7 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
protected int hpMpApUsed, remainingAp;
protected int[] remainingSp = new int[10];
protected transient int clientmaxhp, clientmaxmp, localmaxhp = 50, localmaxmp = 5;
+ protected float transienthp = Float.NEGATIVE_INFINITY, transientmp = Float.NEGATIVE_INFINITY;
private AbstractCharacterListener listener = null;
protected Map statUpdates = new HashMap<>();
@@ -222,6 +223,8 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
} else if (thp > localmaxhp) {
thp = localmaxhp;
}
+
+ if (this.hp != thp) this.transienthp = Float.NEGATIVE_INFINITY;
this.hp = thp;
dispatchHpChanged(oldHp);
@@ -234,6 +237,8 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
} else if (tmp > localmaxmp) {
tmp = localmaxmp;
}
+
+ if (this.mp != tmp) this.transientmp = Float.NEGATIVE_INFINITY;
this.mp = tmp;
}
@@ -246,11 +251,13 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
}
protected void setMaxHp(int hp_) {
+ if (this.maxhp < hp_) this.transienthp = Float.NEGATIVE_INFINITY;
this.maxhp = hp_;
this.clientmaxhp = Math.min(30000, hp_);
}
protected void setMaxMp(int mp_) {
+ if (this.maxmp < mp_) this.transientmp = Float.NEGATIVE_INFINITY;
this.maxmp = mp_;
this.clientmaxmp = Math.min(30000, mp_);
}
diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java
index ef7ad023e4..83cf9af078 100644
--- a/src/client/MapleCharacter.java
+++ b/src/client/MapleCharacter.java
@@ -118,6 +118,7 @@ import client.inventory.MaplePet;
import client.inventory.MapleWeaponType;
import client.inventory.ModifyInventory;
import client.inventory.PetDataFactory;
+import client.inventory.manipulator.MapleCashidGenerator;
import client.inventory.manipulator.MapleInventoryManipulator;
import client.newyear.NewYearCardRecord;
import constants.ExpTable;
@@ -945,7 +946,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public void setMasteries(int jobId) {
int[] skills = new int[4];
for (int i = 0; i > skills.length; i++) {
- skills[i] = 0; //that initialization meng
+ skills[i] = 0; //that initialization meng
}
if (jobId == 112) {
skills[0] = Hero.ACHILLES;
@@ -1002,11 +1003,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
skills[1] = Aran.HIGH_MASTERY;
skills[2] = Aran.FREEZE_STANDING;
} else if (jobId == 2217) {
- skills[0] = Evan.MAPLE_WARRIOR;
- skills[1] = Evan.ILLUSION;
+ skills[0] = Evan.MAPLE_WARRIOR;
+ skills[1] = Evan.ILLUSION;
} else if (jobId == 2218) {
- skills[0] = Evan.BLESSING_OF_THE_ONYX;
- skills[1] = Evan.BLAZE;
+ skills[0] = Evan.BLESSING_OF_THE_ONYX;
+ skills[1] = Evan.BLAZE;
}
for (Integer skillId : skills) {
if (skillId != 0) {
@@ -1341,12 +1342,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
private boolean buffMapProtection() {
- effLock.lock();
- chrLock.lock();
-
int thisMapid = mapid;
int returnMapid = client.getChannelServer().getMapFactory().getMap(thisMapid).getReturnMapId();
+ effLock.lock();
+ chrLock.lock();
try {
for(Entry mbs : effects.entrySet()) {
if(mbs.getKey() == MapleBuffStat.MAP_PROTECTION) {
@@ -1884,7 +1884,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if(hold + quantity <= ii.getSlotMax(client, itemid))
return true;
}
-
+
return getInventory(ItemConstants.getInventoryType(itemid)).getNextFreeSlot() > -1;
}
@@ -1903,7 +1903,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
Skill battleship = SkillFactory.getSkill(Corsair.BATTLE_SHIP);
int cooldown = battleship.getEffect(getSkillLevel(battleship)).getCooldown();
announce(MaplePacketCreator.skillCooldown(Corsair.BATTLE_SHIP, cooldown));
- addCooldown(Corsair.BATTLE_SHIP, System.currentTimeMillis(), (long)(cooldown * 1000));
+ addCooldown(Corsair.BATTLE_SHIP, Server.getInstance().getCurrentTime(), (long)(cooldown * 1000));
removeCooldown(5221999);
cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING);
} else {
@@ -2080,6 +2080,8 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ps3.setInt(1, ringid);
ps3.executeUpdate();
}
+
+ MapleCashidGenerator.freeCashId(ringid);
}
}
}
@@ -2090,11 +2092,13 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ps2.executeUpdate();
}
- if(rs.getInt("petid") > -1) {
+ int petid = rs.getInt("petid");
+ if(petid > -1) {
try (PreparedStatement ps2 = con.prepareStatement("DELETE FROM pets WHERE petid = ?")) {
- ps2.setInt(1, rs.getInt("petid"));
+ ps2.setInt(1, petid);
ps2.executeUpdate();
}
+ MapleCashidGenerator.freeCashId(petid);
}
}
}
@@ -2453,6 +2457,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
chrLock.unlock();
}
+ if (disease == MapleDisease.SEDUCE && chair.get() != 0) {
+ sitChair(0);
+ }
+
final List> debuff = Collections.singletonList(new Pair<>(disease, Integer.valueOf(skill.getX())));
client.announce(MaplePacketCreator.giveDebuff(debuff, skill));
@@ -2550,7 +2558,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public void run() {
doHurtHp();
}
- }, 10000, 10000 - lastHpTask);
+ }, ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL, ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL - lastHpTask);
}
public void resetHpDecreaseTask() {
@@ -2559,7 +2567,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
long lastHpTask = Server.getInstance().getCurrentTime() - lastHpDec;
- startHpDecreaseTask((lastHpTask > 10000) ? 10000 : lastHpTask);
+ startHpDecreaseTask((lastHpTask > ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL) ? ServerConstants.MAP_DAMAGE_OVERTIME_INTERVAL : lastHpTask);
}
public void dropMessage(String message) {
@@ -2682,7 +2690,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
effLock.unlock();
}
- long curTime = System.currentTimeMillis();
+ long curTime = Server.getInstance().getCurrentTime();
for(Entry bel : es) {
MapleCoolDownValueHolder mcdvh = bel.getValue();
if(curTime >= mcdvh.startTime + mcdvh.length) {
@@ -5247,8 +5255,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
this.battleshipHp = (int) length;
addCooldown(skillid, 0, length);
} else {
- int time = (int) ((length + starttime) - System.currentTimeMillis());
- addCooldown(skillid, System.currentTimeMillis(), time);
+ long timeNow = Server.getInstance().getCurrentTime();
+ int time = (int) ((length + starttime) - timeNow);
+ addCooldown(skillid, timeNow, time);
}
}
@@ -5433,9 +5442,43 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
int grade = Math.min(Math.max(level, 30), 120) - 30;
fee += (grade * ServerConstants.BUYBACK_LEVEL_STACK_FEE);
+ if (ServerConstants.USE_BUYBACK_WITH_MESOS) fee *= ServerConstants.BUYBACK_MESO_MULTIPLIER;
+
return (int) Math.floor(fee);
}
+ public void showBuybackInfo() {
+ String s = "#eBUYBACK STATUS#n\r\n\r\nCurrent buyback fee: #b" + getBuybackFee() + " " + (ServerConstants.USE_BUYBACK_WITH_MESOS ? "mesos" : "NX") + "#k\r\n\r\n";
+
+ long timeNow = Server.getInstance().getCurrentTime();
+ boolean avail = true;
+ if (!isAlive()) {
+ long timeLapsed = timeNow - lastDeathtime;
+ long timeRemaining = ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000 - (timeLapsed + Math.max(0, getNextBuybackTime() - timeNow));
+ if (timeRemaining < 1) {
+ s += "Buyback #e#rUNAVAILABLE#k#n";
+ avail = false;
+ } else {
+ s += "Buyback countdown: #e#b" + getTimeRemaining(ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000 - timeLapsed) + "#k#n";
+ }
+ s += "\r\n";
+ }
+
+ if (timeNow < getNextBuybackTime() && avail) {
+ s += "Buyback available in #r" + getTimeRemaining(getNextBuybackTime() - timeNow) + "#k";
+ s += "\r\n";
+ }
+
+ this.showHint(s);
+ }
+
+ private static String getTimeRemaining(long timeLeft) {
+ int seconds = (int) Math.floor(timeLeft / 1000) % 60;
+ int minutes = (int) Math.floor(timeLeft / (1000*60)) % 60;
+
+ return (minutes > 0 ? (String.format("%02d", minutes) + " minutes, ") : "") + String.format("%02d", seconds) + " seconds";
+ }
+
public boolean couldBuyback() { // Ronan's buyback system
long timeNow = Server.getInstance().getCurrentTime();
@@ -5447,16 +5490,12 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
long nextBuybacktime = getNextBuybackTime();
if (timeNow < nextBuybacktime) {
long timeLeft = nextBuybacktime - timeNow;
- int seconds = (int) Math.floor(timeLeft / 1000) % 60;
- int minutes = (int) Math.floor(timeLeft / (1000*60)) % 60;
-
- this.dropMessage(5, "Next buyback available in " + (minutes > 0 ? (String.format("%02d", minutes) + " minutes, ") : "") + String.format("%02d", seconds) + " seconds.");
+ this.dropMessage(5, "Next buyback available in " + getTimeRemaining(timeLeft) + ".");
return false;
}
boolean usingMesos = ServerConstants.USE_BUYBACK_WITH_MESOS;
int fee = getBuybackFee();
- if (usingMesos) fee *= ServerConstants.BUYBACK_MESO_MULTIPLIER;
if (!canBuyback(fee, usingMesos)) {
this.dropMessage(5, "You don't have " + fee + " " + (usingMesos ? "mesos" : "NX") + " to buyback.");
@@ -5528,7 +5567,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
public void leaveMap() {
controlled.clear();
visibleMapObjects.clear();
- chair.set(0);
+ setChair(0);
if (hpDecreaseTask != null) {
hpDecreaseTask.cancel(false);
}
@@ -5710,7 +5749,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
if (ServerConstants.USE_PERFECT_PITCH && level >= 30) {
//milestones?
if (MapleInventoryManipulator.checkSpace(client, 4310000, (short) 1, "")) {
- MapleInventoryManipulator.addById(client, 4310000, (short) 1);
+ MapleInventoryManipulator.addById(client, 4310000, (short) 1, "", -1);
}
} else if (level == 10) {
Runnable r = new Runnable() {
@@ -6452,7 +6491,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ps = con.prepareStatement("SELECT SkillID,StartTime,length FROM cooldowns WHERE charid = ?");
ps.setInt(1, ret.getId());
rs = ps.executeQuery();
- long curTime = System.currentTimeMillis();
+ long curTime = Server.getInstance().getCurrentTime();
while (rs.next()) {
final int skillid = rs.getInt("SkillID");
final long length = rs.getLong("length"), startTime = rs.getLong("StartTime");
@@ -6534,7 +6573,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
ret.storage = MapleStorage.loadOrCreateFromDB(ret.accountid, ret.world);
int startHp = ret.hp, startMp = ret.mp;
- ret.recalcLocalStats();
+ ret.reapplyLocalStats();
ret.changeHpMp(startHp, startMp, true);
//ret.resetBattleshipHp();
}
@@ -6651,7 +6690,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private void playerDead() {
cancelAllBuffs(false);
dispelDebuffs();
- lastDeathtime = System.currentTimeMillis();
+ lastDeathtime = Server.getInstance().getCurrentTime();
EventInstanceManager eim = getEventInstance();
if (eim != null) {
@@ -6702,14 +6741,53 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
cancelEffectFromBuffStat(MapleBuffStat.MONSTER_RIDING);
}
- if (getChair() != 0) {
- setChair(0);
- client.announce(MaplePacketCreator.cancelChair(-1));
- getMap().broadcastMessage(this, MaplePacketCreator.showChair(getId(), 0), false);
- }
+ unsitChairInternal();
client.announce(MaplePacketCreator.enableActions());
}
+ private void unsitChairInternal() {
+ if (chair.get() != 0) {
+ setChair(0);
+ if (unregisterChairBuff()) {
+ getMap().broadcastMessage(this, MaplePacketCreator.cancelForeignChairSkillEffect(this.getId()), false);
+ }
+
+ getMap().broadcastMessage(this, MaplePacketCreator.showChair(this.getId(), 0), false);
+ }
+
+ announce(MaplePacketCreator.cancelChair(-1));
+ }
+
+ public void sitChair(int itemId) {
+ if (client.tryacquireClient()) {
+ try {
+ if (itemId >= 1000000) { // sit on item chair
+ if (chair.get() == 0) {
+ setChair(itemId);
+ getMap().broadcastMessage(this, MaplePacketCreator.showChair(this.getId(), itemId), false);
+ }
+ announce(MaplePacketCreator.enableActions());
+ } else if (itemId != 0) { // sit on map chair
+ if (chair.get() == 0) {
+ setChair(itemId);
+ if (registerChairBuff()) {
+ getMap().broadcastMessage(this, MaplePacketCreator.giveForeignChairSkillEffect(this.getId()), false);
+ }
+ announce(MaplePacketCreator.cancelChair(itemId));
+ }
+ } else { // stand up
+ unsitChairInternal();
+ }
+ } finally {
+ client.releaseClient();
+ }
+ }
+ }
+
+ private void setChair(int chair) {
+ this.chair.set(chair);
+ }
+
public void respawn(int returnMap) {
respawn(null, returnMap); // unspecified EIM, don't force EIM unregister in this case
}
@@ -6780,15 +6858,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
localwatk += equipwatk;
}
- private List> recalcLocalStats() {
+ private void reapplyLocalStats() {
effLock.lock();
+ chrLock.lock();
statWlock.lock();
try {
- List> hpmpupdate = new ArrayList<>(2);
-
- int oldlocalmaxhp = localmaxhp;
- int oldlocalmaxmp = localmaxmp;
-
localmaxhp = getMaxHp();
localmaxmp = getMaxMp();
localdex = getDex();
@@ -6815,11 +6889,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
localmaxhp = Math.min(30000, localmaxhp);
localmaxmp = Math.min(30000, localmaxmp);
- if (ServerConstants.USE_FIXED_RATIO_HPMP_UPDATE) {
- if (localmaxhp != oldlocalmaxhp) hpmpupdate.add(calcHpRatioUpdate(localmaxhp, oldlocalmaxhp));
- if (localmaxmp != oldlocalmaxmp) hpmpupdate.add(calcMpRatioUpdate(localmaxmp, oldlocalmaxmp));
- }
-
MapleStatEffect combo = getBuffEffect(MapleBuffStat.ARAN_COMBO);
if (combo != null) {
localwatk += combo.getX();
@@ -6906,10 +6975,54 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
// Add throwing stars to dmg.
}
+ } finally {
+ statWlock.unlock();
+ chrLock.unlock();
+ effLock.unlock();
+ }
+ }
+
+ private List> recalcLocalStats() {
+ effLock.lock();
+ chrLock.lock();
+ statWlock.lock();
+ try {
+ List> hpmpupdate = new ArrayList<>(2);
+ int oldlocalmaxhp = localmaxhp;
+ int oldlocalmaxmp = localmaxmp;
+
+ reapplyLocalStats();
+
+ if (ServerConstants.USE_FIXED_RATIO_HPMP_UPDATE) {
+ if (localmaxhp != oldlocalmaxhp) {
+ Pair hpUpdate;
+
+ if (transienthp == Float.NEGATIVE_INFINITY) {
+ hpUpdate = calcHpRatioUpdate(localmaxhp, oldlocalmaxhp);
+ } else {
+ hpUpdate = calcHpRatioTransient();
+ }
+
+ hpmpupdate.add(hpUpdate);
+ }
+
+ if (localmaxmp != oldlocalmaxmp) {
+ Pair mpUpdate;
+
+ if (transientmp == Float.NEGATIVE_INFINITY) {
+ mpUpdate = calcMpRatioUpdate(localmaxmp, oldlocalmaxmp);
+ } else {
+ mpUpdate = calcMpRatioTransient();
+ }
+
+ hpmpupdate.add(mpUpdate);
+ }
+ }
return hpmpupdate;
} finally {
statWlock.unlock();
+ chrLock.unlock();
effLock.unlock();
}
}
@@ -7789,10 +7902,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
}
- public void setChair(int chair) {
- this.chair.set(chair);
- }
-
public void setChalkboard(String text) {
this.chalktext = text;
}
@@ -7968,7 +8077,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private Pair calcHpRatioUpdate(int newHp, int oldHp) {
int delta = newHp - oldHp;
- this.hp = calcHpMpRatioUpdate(hp, oldHp, delta);
+ this.hp = calcHpRatioUpdate(hp, oldHp, delta);
hpChangeAction(Short.MIN_VALUE);
return new Pair<>(MapleStat.HP, hp);
@@ -7976,19 +8085,47 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
private Pair calcMpRatioUpdate(int newMp, int oldMp) {
int delta = newMp - oldMp;
- this.mp = calcHpMpRatioUpdate(mp, oldMp, delta);
+ this.mp = calcMpRatioUpdate(mp, oldMp, delta);
return new Pair<>(MapleStat.MP, mp);
}
- private static int calcHpMpRatioUpdate(int curpoint, int maxpoint, int diffpoint) {
+ private static int calcTransientRatio(float transientpoint) {
+ int ret = (int) transientpoint;
+ return !(ret <= 0 && transientpoint > 0.0f) ? ret : 1;
+ }
+
+ private Pair calcHpRatioTransient() {
+ this.hp = calcTransientRatio(transienthp * localmaxhp);
+
+ hpChangeAction(Short.MIN_VALUE);
+ return new Pair<>(MapleStat.HP, hp);
+ }
+
+ private Pair calcMpRatioTransient() {
+ this.mp = calcTransientRatio(transientmp * localmaxmp);
+ return new Pair<>(MapleStat.MP, mp);
+ }
+
+ private int calcHpRatioUpdate(int curpoint, int maxpoint, int diffpoint) {
int curMax = maxpoint;
int nextMax = Math.min(30000, maxpoint + diffpoint);
float temp = curpoint * nextMax;
- int ret = (int) Math.round(temp / curMax);
+ int ret = (int) Math.ceil(temp / curMax);
- //System.out.println("cur: " + curpoint + " next: " + ret + " max: " + curMax + " nextmax:" + nextMax + " diff: " + diffpoint);
- return !(ret <= 0 && curpoint > 0) ? ret : 1;
+ transienthp = (maxpoint > nextMax) ? ((float) curpoint) / maxpoint : ((float) ret) / nextMax;
+ return ret;
+ }
+
+ private int calcMpRatioUpdate(int curpoint, int maxpoint, int diffpoint) {
+ int curMax = maxpoint;
+ int nextMax = Math.min(30000, maxpoint + diffpoint);
+
+ float temp = curpoint * nextMax;
+ int ret = (int) Math.ceil(temp / curMax);
+
+ transientmp = (maxpoint > nextMax) ? ((float) curpoint) / maxpoint : ((float) ret) / nextMax;
+ return ret;
}
public boolean applyHpMpChange(int hpCon, int hpchange, int mpchange) {
@@ -8137,7 +8274,6 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
doorSlot = -1;
party = null;
- //cancelMagicDoor(); // cancel magic doors if kicked out / quitted from party.
} else {
party = p;
}
@@ -9082,11 +9218,27 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
}
pendantExp = 0;
}
+
+ private Collection- getUpgradeableEquipList() {
+ Collection
- fullList = getInventory(MapleInventoryType.EQUIPPED).list();
+ if (ServerConstants.USE_EQUIPMNT_LVLUP_CASH) {
+ return fullList;
+ }
+
+ Collection
- eqpList = new LinkedHashSet<>();
+ for (Item it : fullList) {
+ if (!ii.isCash(it.getItemId())) {
+ eqpList.add(it);
+ }
+ }
+ return eqpList;
+ }
+
public void increaseEquipExp(int expGain) {
if(expGain < 0) expGain = Integer.MAX_VALUE;
- for (Item item : getInventory(MapleInventoryType.EQUIPPED).list()) {
+ for (Item item : getUpgradeableEquipList()) {
Equip nEquip = (Equip) item;
String itemName = ii.getName(nEquip.getItemId());
if (itemName == null) {
diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java
index 451e9a3414..4e1ce4aae8 100644
--- a/src/client/MapleClient.java
+++ b/src/client/MapleClient.java
@@ -41,6 +41,7 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import tools.*;
@@ -109,8 +110,10 @@ public class MapleClient {
private String pic = null;
private String hwid = null;
private int picattempt = 0;
+ private byte csattempt = 0;
private byte gender = -1;
private boolean disconnecting = false;
+ private final Semaphore actionsSemaphore = new Semaphore(7);
private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true);
private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true);
private static final Lock loginLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_LOGIN, true);
@@ -532,7 +535,6 @@ public class MapleClient {
gender = rs.getByte("gender");
characterSlots = rs.getByte("characterslots");
String passhash = rs.getString("password");
- String salt = rs.getString("salt");
byte tos = rs.getByte("tos");
ps.close();
@@ -547,8 +549,9 @@ public class MapleClient {
loginok = 7;
} else if (passhash.charAt(0) == '$' && passhash.charAt(1) == '2' && BCrypt.checkpw(pwd, passhash)) {
loginok = (tos == 0) ? 23 : 0;
- } else if (pwd.equals(passhash) || checkHash(passhash, "SHA-1", pwd) || checkHash(passhash, "SHA-512", pwd + salt)) {
- loginok = (tos == 0) ? -23 : -10; // migrate to bcrypt
+ } else if (pwd.equals(passhash) || checkHash(passhash, "SHA-1", pwd) || checkHash(passhash, "SHA-512", pwd)) {
+ // thanks GabrielSin for detecting some no-bcrypt inconsistencies here
+ loginok = (tos == 0) ? (!ServerConstants.BCRYPT_MIGRATION ? 23 : -23) : (!ServerConstants.BCRYPT_MIGRATION ? 0 : -10); // migrate to bcrypt
} else {
loggedIn = false;
loginok = 4;
@@ -775,7 +778,7 @@ public class MapleClient {
if (!rs.next()) {
rs.close();
ps.close();
- throw new RuntimeException("getLoginState - MapleClient");
+ throw new RuntimeException("getLoginState - MapleClient AccID: " + getAccID());
}
birthday = Calendar.getInstance();
@@ -1170,10 +1173,20 @@ public class MapleClient {
lock.unlock();
}
- public boolean trylockClient() {
- return lock.tryLock();
+ public boolean tryacquireClient() {
+ if (actionsSemaphore.tryAcquire()) {
+ lockClient();
+ return true;
+ } else {
+ return false;
+ }
}
+ public void releaseClient() {
+ unlockClient();
+ actionsSemaphore.release();
+ }
+
public void lockEncoder() {
encoderLock.lock();
}
@@ -1198,7 +1211,7 @@ public class MapleClient {
try {
MessageDigest digester = MessageDigest.getInstance(type);
digester.update(password.getBytes("UTF-8"), 0, password.length());
- return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase().equals(hash);
+ return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase().equals(hash);
} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
throw new RuntimeException("Encoding the string failed", e);
}
@@ -1431,4 +1444,22 @@ public class MapleClient {
this.removeClickedNPC();
NPCScriptManager.getInstance().dispose(this);
}
+
+ public boolean attemptCsCoupon() {
+ if (csattempt > 2) {
+ resetCsCoupon();
+ return false;
+ }
+
+ csattempt++;
+ return true;
+ }
+
+ public void resetCsCoupon() {
+ csattempt = 0;
+ }
+
+ public void enableCSActions() {
+ announce(MaplePacketCreator.enableCSUse(player));
+ }
}
diff --git a/src/client/MapleRing.java b/src/client/MapleRing.java
index e05200fd6b..53111f3fb7 100644
--- a/src/client/MapleRing.java
+++ b/src/client/MapleRing.java
@@ -25,8 +25,9 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
-import java.sql.Statement;
+import tools.Pair;
import tools.DatabaseConnection;
+import client.inventory.manipulator.MapleCashidGenerator;
/**
*
@@ -86,6 +87,9 @@ public class MapleRing implements Comparable {
ps.executeBatch();
ps.close();
+ MapleCashidGenerator.freeCashId(ring.getRingId());
+ MapleCashidGenerator.freeCashId(ring.getPartnerRingId());
+
ps = con.prepareStatement("UPDATE inventoryequipment SET ringid=-1 WHERE ringid=?");
ps.setInt(1, ring.getRingId());
ps.addBatch();
@@ -102,46 +106,40 @@ public class MapleRing implements Comparable {
}
}
- public static int createRing(int itemid, final MapleCharacter partner1, final MapleCharacter partner2) {
+ public static Pair createRing(int itemid, final MapleCharacter partner1, final MapleCharacter partner2) {
try {
if (partner1 == null) {
- return -2;
+ return new Pair<>(-3, -3);
} else if (partner2 == null) {
- return -1;
+ return new Pair<>(-2, -2);
}
+
int[] ringID = new int[2];
+ ringID[0] = MapleCashidGenerator.generateCashId();
+ ringID[1] = MapleCashidGenerator.generateCashId();
+
Connection con = DatabaseConnection.getConnection();
- PreparedStatement ps = con.prepareStatement("INSERT INTO rings (itemid, partnerChrId, partnername) VALUES (?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
- ps.setInt(1, itemid);
- ps.setInt(2, partner2.getId());
- ps.setString(3, partner2.getName());
+ PreparedStatement ps = con.prepareStatement("INSERT INTO rings (id, itemid, partnerRingId, partnerChrId, partnername) VALUES (?, ?, ?, ?, ?)");
+ ps.setInt(1, ringID[0]);
+ ps.setInt(2, itemid);
+ ps.setInt(3, ringID[1]);
+ ps.setInt(4, partner2.getId());
+ ps.setString(5, partner2.getName());
ps.executeUpdate();
- ResultSet rs = ps.getGeneratedKeys();
- rs.next();
- ringID[0] = rs.getInt(1); // ID.
- rs.close();
ps.close();
- ps = con.prepareStatement("INSERT INTO rings (itemid, partnerRingId, partnerChrId, partnername) VALUES (?, ?, ?, ?)", Statement.RETURN_GENERATED_KEYS);
- ps.setInt(1, itemid);
- ps.setInt(2, ringID[0]);
- ps.setInt(3, partner1.getId());
- ps.setString(4, partner1.getName());
- ps.executeUpdate();
- rs = ps.getGeneratedKeys();
- rs.next();
- ringID[1] = rs.getInt(1);
- rs.close();
- ps.close();
- ps = con.prepareStatement("UPDATE rings SET partnerRingId = ? WHERE id = ?");
+ ps = con.prepareStatement("INSERT INTO rings (id, itemid, partnerRingId, partnerChrId, partnername) VALUES (?, ?, ?, ?, ?)");
ps.setInt(1, ringID[1]);
- ps.setInt(2, ringID[0]);
+ ps.setInt(2, itemid);
+ ps.setInt(3, ringID[0]);
+ ps.setInt(4, partner1.getId());
+ ps.setString(5, partner1.getName());
ps.executeUpdate();
ps.close();
con.close();
- return ringID[0];
+ return new Pair<>(ringID[0], ringID[1]);
} catch (SQLException ex) {
ex.printStackTrace();
- return -1;
+ return new Pair<>(-1, -1);
}
}
diff --git a/src/client/autoban/AutobanManager.java b/src/client/autoban/AutobanManager.java
index 83fc5dbd8e..ac27915192 100644
--- a/src/client/autoban/AutobanManager.java
+++ b/src/client/autoban/AutobanManager.java
@@ -8,6 +8,7 @@ package client.autoban;
import client.MapleCharacter;
import java.util.HashMap;
import java.util.Map;
+import net.server.Server;
import tools.FilePrinter;
/**
@@ -35,12 +36,12 @@ public class AutobanManager {
return;
}
if (lastTime.containsKey(fac)) {
- if (lastTime.get(fac) < (System.currentTimeMillis() - fac.getExpire())) {
+ if (lastTime.get(fac) < (Server.getInstance().getCurrentTime() - fac.getExpire())) {
points.put(fac, points.get(fac) / 2); //So the points are not completely gone.
}
}
if (fac.getExpire() != -1)
- lastTime.put(fac, System.currentTimeMillis());
+ lastTime.put(fac, Server.getInstance().getCurrentTime());
if (points.containsKey(fac)) {
points.put(fac, points.get(fac) + 1);
@@ -76,7 +77,7 @@ public class AutobanManager {
//Don't use the same type for more than 1 thing
public void spam(int type) {
- this.spam[type] = System.currentTimeMillis();
+ this.spam[type] = Server.getInstance().getCurrentTime();
}
public void spam(int type, int timestamp) {
diff --git a/src/client/command/CommandsExecutor.java b/src/client/command/CommandsExecutor.java
index a40e14d7e1..da78955c1e 100644
--- a/src/client/command/CommandsExecutor.java
+++ b/src/client/command/CommandsExecutor.java
@@ -162,6 +162,7 @@ public class CommandsExecutor {
addCommand("droplimit", DropLimitCommand.class);
addCommand("time", TimeCommand.class);
addCommand("credits", StaffCommand.class);
+ addCommand("buyback", BuyBackCommand.class);
addCommand("uptime", UptimeCommand.class);
addCommand("gacha", GachaCommand.class);
addCommand("dispose", DisposeCommand.class);
@@ -345,6 +346,7 @@ public class CommandsExecutor {
addCommand("shutdown", 6, ShutdownCommand.class);
addCommand("clearquestcache", 6, ClearQuestCacheCommand.class);
addCommand("clearquest", 6, ClearQuestCommand.class);
+ addCommand("supplyratecoupon", 6, SupplyRateCouponCommand.class);
addCommand("spawnallpnpcs", 6, SpawnAllPNpcsCommand.class);
addCommand("eraseallpnpcs", 6, EraseAllPNpcsCommand.class);
addCommand("addchannel", 6, ServerAddChannelCommand.class);
diff --git a/src/client/command/commands/gm0/BuyBackCommand.java b/src/client/command/commands/gm0/BuyBackCommand.java
index 85ec0e9e04..8c6c9784b7 100644
--- a/src/client/command/commands/gm0/BuyBackCommand.java
+++ b/src/client/command/commands/gm0/BuyBackCommand.java
@@ -30,6 +30,15 @@ import client.processor.BuybackProcessor;
public class BuyBackCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
- BuybackProcessor.processBuyback(c);
+ if (params.length < 1) {
+ c.getPlayer().yellowMessage("Syntax: @buyback ");
+ return;
+ }
+
+ if (params[0].contentEquals("now")) {
+ BuybackProcessor.processBuyback(c);
+ } else {
+ c.getPlayer().showBuybackInfo();
+ }
}
}
diff --git a/src/client/command/commands/gm0/StatDexCommand.java b/src/client/command/commands/gm0/StatDexCommand.java
index 3b14dc0b33..76b974f9e6 100644
--- a/src/client/command/commands/gm0/StatDexCommand.java
+++ b/src/client/command/commands/gm0/StatDexCommand.java
@@ -39,7 +39,7 @@ public class StatDexCommand extends Command {
int remainingAp = player.getRemainingAp();
int amount = (params.length > 0) ? Math.min(Integer.parseInt(params[0]), remainingAp) : Math.min(remainingAp, ServerConstants.MAX_AP - player.getDex());
- if (!player.assignDex(amount)) {
+ if (!player.assignDex(Math.max(amount, 0))) {
player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute.");
}
}
diff --git a/src/client/command/commands/gm0/StatIntCommand.java b/src/client/command/commands/gm0/StatIntCommand.java
index 21a347d8f2..02ab5b6f78 100644
--- a/src/client/command/commands/gm0/StatIntCommand.java
+++ b/src/client/command/commands/gm0/StatIntCommand.java
@@ -39,7 +39,7 @@ public class StatIntCommand extends Command {
int remainingAp = player.getRemainingAp();
int amount = (params.length > 0) ? Math.min(Integer.parseInt(params[0]), remainingAp) : Math.min(remainingAp, ServerConstants.MAX_AP - player.getInt());
- if (!player.assignInt(amount)) {
+ if (!player.assignInt(Math.max(amount, 0))) {
player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute.");
}
}
diff --git a/src/client/command/commands/gm0/StatLukCommand.java b/src/client/command/commands/gm0/StatLukCommand.java
index 03a41e0341..f2a97c8bba 100644
--- a/src/client/command/commands/gm0/StatLukCommand.java
+++ b/src/client/command/commands/gm0/StatLukCommand.java
@@ -39,7 +39,7 @@ public class StatLukCommand extends Command {
int remainingAp = player.getRemainingAp();
int amount = (params.length > 0) ? Math.min(Integer.parseInt(params[0]), remainingAp) : Math.min(remainingAp, ServerConstants.MAX_AP - player.getLuk());
- if (!player.assignLuk(amount)) {
+ if (!player.assignLuk(Math.max(amount, 0))) {
player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute.");
}
}
diff --git a/src/client/command/commands/gm0/StatStrCommand.java b/src/client/command/commands/gm0/StatStrCommand.java
index ce288f27f3..46bac383a8 100644
--- a/src/client/command/commands/gm0/StatStrCommand.java
+++ b/src/client/command/commands/gm0/StatStrCommand.java
@@ -39,7 +39,7 @@ public class StatStrCommand extends Command {
int remainingAp = player.getRemainingAp();
int amount = (params.length > 0) ? Math.min(Integer.parseInt(params[0]), remainingAp) : Math.min(remainingAp, ServerConstants.MAX_AP - player.getStr());
- if (!player.assignStr(amount)) {
+ if (!player.assignStr(Math.max(amount, 0))) {
player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute.");
}
}
diff --git a/src/client/command/commands/gm1/WhoDropsCommand.java b/src/client/command/commands/gm1/WhoDropsCommand.java
index ff130495dc..9d4a428007 100644
--- a/src/client/command/commands/gm1/WhoDropsCommand.java
+++ b/src/client/command/commands/gm1/WhoDropsCommand.java
@@ -49,40 +49,49 @@ public class WhoDropsCommand extends Command {
player.dropMessage(5, "Please do @whodrops
- ");
return;
}
- String searchString = joinStringFrom(params, 0);
- String output = "";
- Iterator> listIterator = MapleItemInformationProvider.getInstance().getItemDataByName(searchString).iterator();
- if(listIterator.hasNext()) {
- int count = 1;
- while(listIterator.hasNext() && count <= 3) {
- Pair data = listIterator.next();
- output += "#b" + data.getRight() + "#k is dropped by:\r\n";
- try {
- Connection con = DatabaseConnection.getConnection();
- PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE itemid = ? LIMIT 50");
- ps.setInt(1, data.getLeft());
- ResultSet rs = ps.executeQuery();
- while(rs.next()) {
- String resultName = MapleMonsterInformationProvider.getMobNameFromID(rs.getInt("dropperid"));
- if (resultName != null) {
- output += resultName + ", ";
+
+ if (c.tryacquireClient()) {
+ try {
+ String searchString = joinStringFrom(params, 0);
+ String output = "";
+ Iterator> listIterator = MapleItemInformationProvider.getInstance().getItemDataByName(searchString).iterator();
+ if(listIterator.hasNext()) {
+ int count = 1;
+ while(listIterator.hasNext() && count <= 3) {
+ Pair data = listIterator.next();
+ output += "#b" + data.getRight() + "#k is dropped by:\r\n";
+ try {
+ Connection con = DatabaseConnection.getConnection();
+ PreparedStatement ps = con.prepareStatement("SELECT dropperid FROM drop_data WHERE itemid = ? LIMIT 50");
+ ps.setInt(1, data.getLeft());
+ ResultSet rs = ps.executeQuery();
+ while(rs.next()) {
+ String resultName = MapleMonsterInformationProvider.getMobNameFromID(rs.getInt("dropperid"));
+ if (resultName != null) {
+ output += resultName + ", ";
+ }
+ }
+ rs.close();
+ ps.close();
+ con.close();
+ } catch (Exception e) {
+ player.dropMessage(6, "There was a problem retrieving the required data. Please try again.");
+ e.printStackTrace();
+ return;
}
+ output += "\r\n\r\n";
+ count++;
}
- rs.close();
- ps.close();
- con.close();
- } catch (Exception e) {
- player.dropMessage(6, "There was a problem retrieving the required data. Please try again.");
- e.printStackTrace();
+ } else {
+ player.dropMessage(5, "The item you searched for doesn't exist.");
return;
}
- output += "\r\n\r\n";
- count++;
+ c.announce(MaplePacketCreator.getNPCTalk(9010000, (byte) 0, output, "00 00", (byte) 0));
+ } finally {
+ c.releaseClient();
}
} else {
- player.dropMessage(5, "The item you searched for doesn't exist.");
- return;
+ player.dropMessage(5, "Please wait a while for your request to be processed.");
}
- c.announce(MaplePacketCreator.getNPCTalk(9010000, (byte) 0, output, "00 00", (byte) 0));
}
}
diff --git a/src/client/command/commands/gm2/ItemDropCommand.java b/src/client/command/commands/gm2/ItemDropCommand.java
index ac75522174..892a0cda7b 100644
--- a/src/client/command/commands/gm2/ItemDropCommand.java
+++ b/src/client/command/commands/gm2/ItemDropCommand.java
@@ -92,7 +92,6 @@ public class ItemDropCommand extends Command {
return;
}
}
-
Item toDrop;
if (ItemConstants.getInventoryType(itemId) == MapleInventoryType.EQUIP) {
diff --git a/src/client/command/commands/gm2/SearchCommand.java b/src/client/command/commands/gm2/SearchCommand.java
index 4fa3d31e35..6873c8b4ae 100644
--- a/src/client/command/commands/gm2/SearchCommand.java
+++ b/src/client/command/commands/gm2/SearchCommand.java
@@ -37,8 +37,19 @@ import tools.Pair;
import java.io.File;
public class SearchCommand extends Command {
+ private static MapleData npcStringData;
+ private static MapleData mobStringData;
+ private static MapleData skillStringData;
+ private static MapleData mapStringData;
+
{
setDescription("");
+
+ MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
+ npcStringData = dataProvider.getData("Npc.img");
+ mobStringData = dataProvider.getData("Mob.img");
+ skillStringData = dataProvider.getData("Skill.img");
+ mapStringData = dataProvider.getData("Map.img");
}
@Override
@@ -53,26 +64,43 @@ public class SearchCommand extends Command {
String search = joinStringFrom(params,1);
long start = System.currentTimeMillis();//for the lulz
MapleData data = null;
- MapleDataProvider dataProvider = MapleDataProviderFactory.getDataProvider(new File("wz/String.wz"));
if (!params[0].equalsIgnoreCase("ITEM")) {
+ boolean mapSearch = false;
+
if (params[0].equalsIgnoreCase("NPC")) {
- data = dataProvider.getData("Npc.img");
+ data = npcStringData;
} else if (params[0].equalsIgnoreCase("MOB") || params[0].equalsIgnoreCase("MONSTER")) {
- data = dataProvider.getData("Mob.img");
+ data = mobStringData;
} else if (params[0].equalsIgnoreCase("SKILL")) {
- data = dataProvider.getData("Skill.img");
- /*} else if (sub[1].equalsIgnoreCase("MAP")) {
- TODO
- */
+ data = skillStringData;
+ } else if (params[0].equalsIgnoreCase("MAP")) {
+ data = mapStringData;
+ mapSearch = true;
} else {
- sb.append("#bInvalid search.\r\nSyntax: '!search [type] [name]', where [type] is NPC, ITEM, MOB, or SKILL.");
+ sb.append("#bInvalid search.\r\nSyntax: '!search [type] [name]', where [type] is MAP, NPC, ITEM, MOB, or SKILL.");
}
if (data != null) {
String name;
- for (MapleData searchData : data.getChildren()) {
- name = MapleDataTool.getString(searchData.getChildByPath("name"), "NO-NAME");
- if (name.toLowerCase().contains(search.toLowerCase())) {
- sb.append("#b").append(Integer.parseInt(searchData.getName())).append("#k - #r").append(name).append("\r\n");
+
+ if (!mapSearch) {
+ for (MapleData searchData : data.getChildren()) {
+ name = MapleDataTool.getString(searchData.getChildByPath("name"), "NO-NAME");
+ if (name.toLowerCase().contains(search.toLowerCase())) {
+ sb.append("#b").append(Integer.parseInt(searchData.getName())).append("#k - #r").append(name).append("\r\n");
+ }
+ }
+ } else {
+ String mapName, streetName;
+
+ for (MapleData searchDataDir : data.getChildren()) {
+ for (MapleData searchData : searchDataDir.getChildren()) {
+ mapName = MapleDataTool.getString(searchData.getChildByPath("mapName"), "NO-NAME");
+ streetName = MapleDataTool.getString(searchData.getChildByPath("streetName"), "NO-NAME");
+
+ if (mapName.toLowerCase().contains(search.toLowerCase()) || streetName.toLowerCase().contains(search.toLowerCase())) {
+ sb.append("#b").append(Integer.parseInt(searchData.getName())).append("#k - #r").append(streetName).append(" - ").append(mapName).append("\r\n");
+ }
+ }
}
}
}
diff --git a/src/client/command/commands/gm6/FredCommand.java b/src/client/command/commands/gm6/SupplyRateCouponCommand.java
similarity index 68%
rename from src/client/command/commands/gm6/FredCommand.java
rename to src/client/command/commands/gm6/SupplyRateCouponCommand.java
index 8e6722fac6..17a4defbab 100644
--- a/src/client/command/commands/gm6/FredCommand.java
+++ b/src/client/command/commands/gm6/SupplyRateCouponCommand.java
@@ -17,25 +17,27 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see .
*/
-
-/*
- @Author: Arthur L - Refactored command content into modules
-*/
package client.command.commands.gm6;
-import client.command.Command;
-import client.MapleClient;
import client.MapleCharacter;
-import tools.MaplePacketCreator;
+import client.MapleClient;
+import client.command.Command;
+import constants.ServerConstants;
-public class FredCommand extends Command {
+public class SupplyRateCouponCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
- c.announce(MaplePacketCreator.fredrickMessage(Byte.valueOf(params[0])));
-
+ MapleCharacter player = c.getPlayer();
+ if (params.length < 1) {
+ player.dropMessage(5, "Syntax: !supplyratecoupon ");
+ return;
+ }
+
+ ServerConstants.USE_SUPPLY_RATE_COUPONS = params[0].compareToIgnoreCase("no") != 0;
+ player.dropMessage(5, "Rate coupons are now " + (ServerConstants.USE_SUPPLY_RATE_COUPONS ? "enabled" : "disabled") + " for purchase at the Cash Shop.");
}
}
diff --git a/src/client/inventory/Item.java b/src/client/inventory/Item.java
index ad0ca26ca7..c9b8691dd1 100644
--- a/src/client/inventory/Item.java
+++ b/src/client/inventory/Item.java
@@ -26,10 +26,12 @@ import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
+import client.inventory.manipulator.MapleKarmaManipulator;
+import server.MapleItemInformationProvider;
public class Item implements Comparable
- {
- private static AtomicInteger runningCashId = new AtomicInteger(0);
+ private static AtomicInteger runningCashId = new AtomicInteger(777000000); // pets & rings shares cashid values
private int id, cashId, sn;
private short position;
@@ -83,7 +85,7 @@ public class Item implements Comparable
- {
public int getCashId() {
if (cashId == 0) {
- cashId = runningCashId.incrementAndGet();
+ cashId = runningCashId.getAndIncrement();
}
return cashId;
}
@@ -177,4 +179,8 @@ public class Item implements Comparable
- {
public MaplePet getPet() {
return pet;
}
+
+ public boolean isUntradeable() {
+ return ((this.getFlag() & ItemConstants.UNTRADEABLE) == ItemConstants.UNTRADEABLE) || (MapleItemInformationProvider.getInstance().isDropRestricted(this.getItemId()) && !MapleKarmaManipulator.hasKarmaFlag(this));
+ }
}
diff --git a/src/client/inventory/MaplePet.java b/src/client/inventory/MaplePet.java
index 4d6dbd1423..8f320e5565 100644
--- a/src/client/inventory/MaplePet.java
+++ b/src/client/inventory/MaplePet.java
@@ -21,7 +21,6 @@
*/
package client.inventory;
-import com.mysql.jdbc.Statement;
import constants.ExpTable;
import java.awt.Point;
import java.sql.PreparedStatement;
@@ -34,6 +33,7 @@ import server.movement.AbsoluteLifeMovement;
import server.movement.LifeMovement;
import server.movement.LifeMovementFragment;
import client.MapleCharacter;
+import client.inventory.manipulator.MapleCashidGenerator;
import java.sql.Connection;
import tools.MaplePacketCreator;
import tools.Pair;
@@ -90,6 +90,8 @@ public class MaplePet extends Item {
ps.executeUpdate();
ps.close();
con.close();
+
+ MapleCashidGenerator.freeCashId(this.getUniqueId());
} catch (SQLException ex) {
ex.printStackTrace();
}
@@ -116,15 +118,11 @@ public class MaplePet extends Item {
public static int createPet(int itemid) {
try {
Connection con = DatabaseConnection.getConnection();
- PreparedStatement ps = con.prepareStatement("INSERT INTO pets (name, level, closeness, fullness, summoned) VALUES (?, 1, 0, 100, 0)", Statement.RETURN_GENERATED_KEYS);
- ps.setString(1, MapleItemInformationProvider.getInstance().getName(itemid));
+ PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned) VALUES (?, ?, 1, 0, 100, 0)");
+ int ret = MapleCashidGenerator.generateCashId();
+ ps.setInt(1, ret);
+ ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid));
ps.executeUpdate();
- ResultSet rs = ps.getGeneratedKeys();
- int ret = -1;
- if (rs.next()) {
- ret = rs.getInt(1);
- }
- rs.close();
ps.close();
con.close();
return ret;
@@ -137,18 +135,14 @@ public class MaplePet extends Item {
public static int createPet(int itemid, byte level, int closeness, int fullness) {
try {
Connection con = DatabaseConnection.getConnection();
- PreparedStatement ps = con.prepareStatement("INSERT INTO pets (name, level, closeness, fullness, summoned) VALUES (?, ?, ?, ?, 0)", Statement.RETURN_GENERATED_KEYS);
- ps.setString(1, MapleItemInformationProvider.getInstance().getName(itemid));
- ps.setByte(2, level);
- ps.setInt(3, closeness);
- ps.setInt(4, fullness);
+ PreparedStatement ps = con.prepareStatement("INSERT INTO pets (petid, name, level, closeness, fullness, summoned) VALUES (?, ?, ?, ?, ?, 0)");
+ int ret = MapleCashidGenerator.generateCashId();
+ ps.setInt(1, ret);
+ ps.setString(2, MapleItemInformationProvider.getInstance().getName(itemid));
+ ps.setByte(3, level);
+ ps.setInt(4, closeness);
+ ps.setInt(5, fullness);
ps.executeUpdate();
- ResultSet rs = ps.getGeneratedKeys();
- int ret = -1;
- if (rs.next()) {
- ret = rs.getInt(1);
- }
- rs.close();
ps.close();
con.close();
return ret;
diff --git a/src/client/inventory/manipulator/MapleCashidGenerator.java b/src/client/inventory/manipulator/MapleCashidGenerator.java
new file mode 100644
index 0000000000..cba7bf1e18
--- /dev/null
+++ b/src/client/inventory/manipulator/MapleCashidGenerator.java
@@ -0,0 +1,104 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft (L) 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package client.inventory.manipulator;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashSet;
+import java.util.Set;
+import tools.DatabaseConnection;
+
+/**
+ *
+ * @author RonanLana
+ */
+public class MapleCashidGenerator {
+
+ private final static Set existentCashids = new HashSet<>(10000);
+ private static Integer runningCashid = 0;
+
+ private static void loadExistentCashIdsFromQuery(Connection con, String query) throws SQLException {
+ PreparedStatement ps = con.prepareStatement(query);
+ ResultSet rs = ps.executeQuery();
+
+ while (rs.next()) {
+ existentCashids.add(rs.getInt(1));
+ }
+
+ rs.close();
+ ps.close();
+ }
+
+ public static synchronized void loadExistentCashIdsFromDb() {
+ Connection con = null;
+ try {
+ con = DatabaseConnection.getConnection();
+
+ loadExistentCashIdsFromQuery(con, "SELECT id FROM rings");
+ loadExistentCashIdsFromQuery(con, "SELECT petid FROM pets");
+
+ con.close();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ } finally {
+ try {
+ if (con != null && !con.isClosed()) {
+ con.close();
+ }
+ } catch (SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ runningCashid = 0;
+ do {
+ runningCashid++; // hopefully the id will never surpass the allotted amount for pets/rings?
+ } while (existentCashids.contains(runningCashid));
+ }
+
+ private static void getNextAvailableCashId() {
+ runningCashid++;
+ if (runningCashid >= 777000000) {
+ existentCashids.clear();
+ loadExistentCashIdsFromDb();
+ }
+ }
+
+ public static synchronized int generateCashId() {
+ while (true) {
+ if (!existentCashids.contains(runningCashid)) {
+ int ret = runningCashid;
+ getNextAvailableCashId();
+
+ // existentCashids.add(ret)... no need to do this since the wrap over already refetches already used cashids from the DB
+ return ret;
+ }
+
+ getNextAvailableCashId();
+ }
+ }
+
+ public static synchronized void freeCashId(int cashId) {
+ existentCashids.remove(cashId);
+ }
+
+}
diff --git a/src/client/inventory/manipulator/MapleInventoryManipulator.java b/src/client/inventory/manipulator/MapleInventoryManipulator.java
index e85f4d8048..e26f36824a 100644
--- a/src/client/inventory/manipulator/MapleInventoryManipulator.java
+++ b/src/client/inventory/manipulator/MapleInventoryManipulator.java
@@ -520,16 +520,18 @@ public class MapleInventoryManipulator {
//1112413, 1112414, 1112405 (Lilin's Ring)
source = (Equip) eqpInv.getItem(src);
- Equip target = (Equip) eqpdInv.getItem(dst);
eqpInv.removeSlot(src);
- if (target != null) {
- eqpdInv.lockInventory();
- try {
+
+ Equip target;
+ eqpdInv.lockInventory();
+ try {
+ target = (Equip) eqpdInv.getItem(dst);
+ if (target != null) {
chr.unequippedItem(target);
eqpdInv.removeSlot(dst);
- } finally {
- eqpdInv.unlockInventory();
}
+ } finally {
+ eqpdInv.unlockInventory();
}
final List mods = new ArrayList<>();
@@ -705,7 +707,7 @@ public class MapleInventoryManipulator {
}
private static boolean isDroppedItemRestricted(Item it) {
- return ServerConstants.USE_ERASE_UNTRADEABLE_DROP && ((it.getFlag() & ItemConstants.UNTRADEABLE) == ItemConstants.UNTRADEABLE);
+ return ServerConstants.USE_ERASE_UNTRADEABLE_DROP && it.isUntradeable();
}
public static boolean isSandboxItem(Item it) {
diff --git a/src/client/processor/AssignSPProcessor.java b/src/client/processor/AssignSPProcessor.java
index 5d263bfe40..979f1439ba 100644
--- a/src/client/processor/AssignSPProcessor.java
+++ b/src/client/processor/AssignSPProcessor.java
@@ -43,7 +43,7 @@ public class AssignSPProcessor {
c.lockClient();
try {
if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
- c.getSession().write(MaplePacketCreator.enableActions());
+ c.announce(MaplePacketCreator.enableActions());
return;
}
diff --git a/src/client/processor/BuybackProcessor.java b/src/client/processor/BuybackProcessor.java
index e4f6f5f238..82cb5300dd 100644
--- a/src/client/processor/BuybackProcessor.java
+++ b/src/client/processor/BuybackProcessor.java
@@ -21,16 +21,13 @@ package client.processor;
import client.MapleClient;
import client.MapleCharacter;
-import client.MapleStat;
import java.awt.Point;
-import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import server.maps.MapleMap;
import server.movement.AbsoluteLifeMovement;
import server.movement.LifeMovementFragment;
import tools.MaplePacketCreator;
-import tools.Pair;
/**
*
diff --git a/src/client/processor/DueyProcessor.java b/src/client/processor/DueyProcessor.java
index b6ffb987e6..d24b91cf6d 100644
--- a/src/client/processor/DueyProcessor.java
+++ b/src/client/processor/DueyProcessor.java
@@ -345,167 +345,171 @@ public class DueyProcessor {
}
public static void dueySendItem(MapleClient c, byte inventId, short itemPos, short amount, int mesos, String recipient) {
- c.lockClient();
- try {
- final int fee = 5000;
- final long sendMesos = (long) mesos + fee;
- if (mesos < 0 || sendMesos > Integer.MAX_VALUE || (amount < 1 && mesos == 0)) {
- AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with duey.");
- FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use duey with mesos " + mesos + " and amount " + amount + "\r\n");
- c.disconnect(true, false);
- return;
- }
-
- int finalcost = mesos + fee;
- if (c.getPlayer().getMeso() >= finalcost) {
- int accid = getAccIdFromCNAME(recipient, true);
- if (accid != -1) {
- if (accid == c.getAccID()) {
- c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SAMEACC_ERROR.getCode()));
+ if (c.tryacquireClient()) {
+ try {
+ final int fee = 5000;
+ final long sendMesos = (long) mesos + fee;
+ if (mesos < 0 || sendMesos > Integer.MAX_VALUE || (amount < 1 && mesos == 0)) {
+ AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with duey.");
+ FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use duey with mesos " + mesos + " and amount " + amount + "\r\n");
+ c.disconnect(true, false);
+ return;
+ }
+
+ int finalcost = mesos + fee;
+ if (c.getPlayer().getMeso() >= finalcost) {
+ int accid = getAccIdFromCNAME(recipient, true);
+ if (accid != -1) {
+ if (accid == c.getAccID()) {
+ c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SAMEACC_ERROR.getCode()));
+ return;
+ }
+ } else {
+ c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NAME_DOES_NOT_EXIST.getCode()));
return;
}
} else {
- c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NAME_DOES_NOT_EXIST.getCode()));
+ c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NOT_ENOUGH_MESOS.getCode()));
return;
}
- } else {
- c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_NOT_ENOUGH_MESOS.getCode()));
- return;
- }
- MapleClient rClient = null;
- int channel = c.getWorldServer().find(recipient);
- if (channel > -1) {
- Channel rcserv = c.getWorldServer().getChannel(channel);
- if(rcserv != null) {
- MapleCharacter rChr = rcserv.getPlayerStorage().getCharacterByName(recipient);
- if(rChr != null) {
- rClient = rChr.getClient();
+ MapleClient rClient = null;
+ int channel = c.getWorldServer().find(recipient);
+ if (channel > -1) {
+ Channel rcserv = c.getWorldServer().getChannel(channel);
+ if(rcserv != null) {
+ MapleCharacter rChr = rcserv.getPlayerStorage().getCharacterByName(recipient);
+ if(rChr != null) {
+ rClient = rChr.getClient();
+ }
}
}
- }
-
- if (inventId > 0) {
- MapleInventoryType inv = MapleInventoryType.getByType(inventId);
- Item item = c.getPlayer().getInventory(inv).getItem(itemPos);
- if (item != null && c.getPlayer().getItemQuantity(item.getItemId(), false) >= amount) {
- c.getPlayer().gainMeso(-finalcost, false);
- c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SUCCESSFULLY_SENT.getCode()));
- if (ItemConstants.isRechargeable(item.getItemId())) {
- MapleInventoryManipulator.removeFromSlot(c, inv, itemPos, item.getQuantity(), true);
+ if (inventId > 0) {
+ MapleInventoryType inv = MapleInventoryType.getByType(inventId);
+ Item item = c.getPlayer().getInventory(inv).getItem(itemPos);
+ if (item != null && c.getPlayer().getItemQuantity(item.getItemId(), false) >= amount) {
+ c.getPlayer().gainMeso(-finalcost, false);
+ c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SUCCESSFULLY_SENT.getCode()));
+
+ if (ItemConstants.isRechargeable(item.getItemId())) {
+ MapleInventoryManipulator.removeFromSlot(c, inv, itemPos, item.getQuantity(), true);
+ } else {
+ MapleInventoryManipulator.removeFromSlot(c, inv, itemPos, amount, true, false);
+ }
+
+ MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
+ addItemToDB(item, amount, mesos - getFee(mesos), c.getPlayer().getName(), getAccIdFromCNAME(recipient, false));
} else {
- MapleInventoryManipulator.removeFromSlot(c, inv, itemPos, amount, true, false);
+ if (item != null) {
+ c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_INCORRECT_REQUEST.getCode()));
+ }
+ return;
}
-
- MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
- addItemToDB(item, amount, mesos - getFee(mesos), c.getPlayer().getName(), getAccIdFromCNAME(recipient, false));
} else {
- if (item != null) {
- c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_INCORRECT_REQUEST.getCode()));
- }
- return;
+ c.getPlayer().gainMeso(-finalcost, false);
+ c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SUCCESSFULLY_SENT.getCode()));
+
+ addMesoToDB(mesos - getFee(mesos), c.getPlayer().getName(), getAccIdFromCNAME(recipient, false));
}
- } else {
- c.getPlayer().gainMeso(-finalcost, false);
- c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_SUCCESSFULLY_SENT.getCode()));
- addMesoToDB(mesos - getFee(mesos), c.getPlayer().getName(), getAccIdFromCNAME(recipient, false));
+ if (rClient != null && rClient.isLoggedIn() && !rClient.getPlayer().isAwayFromWorld()) {
+ showDueyNotification(rClient, rClient.getPlayer());
+ }
+ } finally {
+ c.releaseClient();
}
-
- if (rClient != null && rClient.isLoggedIn() && !rClient.getPlayer().isAwayFromWorld()) {
- showDueyNotification(rClient, rClient.getPlayer());
- }
- } finally {
- c.unlockClient();
}
}
public static void dueyRemovePackage(MapleClient c, int packageid) {
- c.lockClient();
- try {
- removeItemFromDB(packageid);
- c.announce(MaplePacketCreator.removeItemFromDuey(true, packageid));
- } finally {
- c.unlockClient();
+ if (c.tryacquireClient()) {
+ try {
+ removeItemFromDB(packageid);
+ c.announce(MaplePacketCreator.removeItemFromDuey(true, packageid));
+ } finally {
+ c.releaseClient();
+ }
}
}
public static void dueyClaimPackage(MapleClient c, int packageid) {
- c.lockClient();
- try {
- List packages = new LinkedList<>();
- DueyPackages dp = null;
- Connection con = null;
+ if (c.tryacquireClient()) {
try {
- con = DatabaseConnection.getConnection();
- DueyPackages dueypack;
- try (PreparedStatement ps = con.prepareStatement("SELECT * FROM dueypackages LEFT JOIN dueyitems USING (PackageId) WHERE PackageId = ?")) {
- ps.setInt(1, packageid);
- try (ResultSet rs = ps.executeQuery()) {
- dueypack = null;
- if (rs.next()) {
- dueypack = getItemByPID(rs);
- dueypack.setSender(rs.getString("SenderName"));
- dueypack.setMesos(rs.getInt("Mesos"));
- dueypack.setSentTime(rs.getString("TimeStamp"));
+ List packages = new LinkedList<>();
+ DueyPackages dp = null;
+ Connection con = null;
+ try {
+ con = DatabaseConnection.getConnection();
+ DueyPackages dueypack;
+ try (PreparedStatement ps = con.prepareStatement("SELECT * FROM dueypackages LEFT JOIN dueyitems USING (PackageId) WHERE PackageId = ?")) {
+ ps.setInt(1, packageid);
+ try (ResultSet rs = ps.executeQuery()) {
+ dueypack = null;
+ if (rs.next()) {
+ dueypack = getItemByPID(rs);
+ dueypack.setSender(rs.getString("SenderName"));
+ dueypack.setMesos(rs.getInt("Mesos"));
+ dueypack.setSentTime(rs.getString("TimeStamp"));
- packages.add(dueypack);
+ packages.add(dueypack);
+ }
}
}
- }
- dp = dueypack;
- if(dp == null) {
- c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode()));
- FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to receive package from duey with id " + packageid + "\r\n");
- return;
- }
-
- if (dp.getItem() != null) {
- if (!MapleInventoryManipulator.checkSpace(c, dp.getItem().getItemId(), dp.getItem().getQuantity(), dp.getItem().getOwner())) {
- int itemid = dp.getItem().getItemId();
- if(MapleItemInformationProvider.getInstance().isPickupRestricted(itemid) && c.getPlayer().getInventory(ItemConstants.getInventoryType(itemid)).findById(itemid) != null) {
- c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_RECEIVER_WITH_UNIQUE.getCode()));
- } else {
- c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_NO_FREE_SLOTS.getCode()));
- }
-
+ dp = dueypack;
+ if(dp == null) {
+ c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode()));
+ FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to receive package from duey with id " + packageid + "\r\n");
return;
- } else {
- MapleInventoryManipulator.addFromDrop(c, dp.getItem(), false);
}
+
+ if (dp.getItem() != null) {
+ if (!MapleInventoryManipulator.checkSpace(c, dp.getItem().getItemId(), dp.getItem().getQuantity(), dp.getItem().getOwner())) {
+ int itemid = dp.getItem().getItemId();
+ if(MapleItemInformationProvider.getInstance().isPickupRestricted(itemid) && c.getPlayer().getInventory(ItemConstants.getInventoryType(itemid)).findById(itemid) != null) {
+ c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_RECEIVER_WITH_UNIQUE.getCode()));
+ } else {
+ c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_NO_FREE_SLOTS.getCode()));
+ }
+
+ return;
+ } else {
+ MapleInventoryManipulator.addFromDrop(c, dp.getItem(), false);
+ }
+ }
+
+ long gainmesos;
+ long totalmesos = (long) dp.getMesos() + c.getPlayer().getMeso();
+
+ if (totalmesos < 0 || dp.getMesos() < 0) {
+ gainmesos = 0;
+ } else {
+ totalmesos = Math.min(totalmesos, Integer.MAX_VALUE);
+ gainmesos = totalmesos - c.getPlayer().getMeso();
+ }
+
+ c.getPlayer().gainMeso((int)gainmesos, false);
+
+ removeItemFromDB(packageid);
+ c.announce(MaplePacketCreator.removeItemFromDuey(false, packageid));
+
+ con.close();
+ } catch (SQLException e) {
+ e.printStackTrace();
}
-
- long gainmesos;
- long totalmesos = (long) dp.getMesos() + c.getPlayer().getMeso();
-
- if (totalmesos < 0 || dp.getMesos() < 0) {
- gainmesos = 0;
- } else {
- totalmesos = Math.min(totalmesos, Integer.MAX_VALUE);
- gainmesos = totalmesos - c.getPlayer().getMeso();
- }
-
- c.getPlayer().gainMeso((int)gainmesos, false);
-
- removeItemFromDB(packageid);
- c.announce(MaplePacketCreator.removeItemFromDuey(false, packageid));
-
- con.close();
- } catch (SQLException e) {
- e.printStackTrace();
+ } finally {
+ c.releaseClient();
}
- } finally {
- c.unlockClient();
}
}
public static void dueySendTalk(MapleClient c) {
- c.lockClient();
- try {
- c.announce(MaplePacketCreator.sendDuey((byte) 8, loadItems(c.getPlayer())));
- } finally {
- c.unlockClient();
+ if (c.tryacquireClient()) {
+ try {
+ c.announce(MaplePacketCreator.sendDuey((byte) 8, loadItems(c.getPlayer())));
+ } finally {
+ c.releaseClient();
+ }
}
}
}
diff --git a/src/client/processor/FredrickProcessor.java b/src/client/processor/FredrickProcessor.java
index 0944b39f69..780a19bab9 100644
--- a/src/client/processor/FredrickProcessor.java
+++ b/src/client/processor/FredrickProcessor.java
@@ -70,42 +70,43 @@ public class FredrickProcessor {
}
public static void fredrickRetrieveItems(MapleClient c) { // thanks Gustav for pointing out the dupe on Fredrick handling
- c.lockClient();
- try {
- MapleCharacter chr = c.getPlayer();
-
- List> items;
+ if (c.tryacquireClient()) {
try {
- items = ItemFactory.MERCHANT.loadItems(chr.getId(), false);
- if (!canRetrieveFromFredrick(chr, items)) {
- chr.announce(MaplePacketCreator.fredrickMessage((byte) 0x21));
- return;
- }
+ MapleCharacter chr = c.getPlayer();
- chr.withdrawMerchantMesos();
-
- if (deleteFredrickItems(chr.getId())) {
- MapleHiredMerchant merchant = chr.getHiredMerchant();
-
- if(merchant != null)
- merchant.clearItems();
-
- for (Pair
- it : items) {
- Item item = it.getLeft();
- MapleInventoryManipulator.addFromDrop(chr.getClient(), item, false);
- String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
- FilePrinter.print(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
+ List> items;
+ try {
+ items = ItemFactory.MERCHANT.loadItems(chr.getId(), false);
+ if (!canRetrieveFromFredrick(chr, items)) {
+ chr.announce(MaplePacketCreator.fredrickMessage((byte) 0x21));
+ return;
}
- chr.announce(MaplePacketCreator.fredrickMessage((byte) 0x1E));
- } else {
- chr.message("An unknown error has occured.");
+ chr.withdrawMerchantMesos();
+
+ if (deleteFredrickItems(chr.getId())) {
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+
+ if(merchant != null)
+ merchant.clearItems();
+
+ for (Pair
- it : items) {
+ Item item = it.getLeft();
+ MapleInventoryManipulator.addFromDrop(chr.getClient(), item, false);
+ String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
+ FilePrinter.print(FilePrinter.FREDRICK + chr.getName() + ".txt", chr.getName() + " gained " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
+ }
+
+ chr.announce(MaplePacketCreator.fredrickMessage((byte) 0x1E));
+ } else {
+ chr.message("An unknown error has occured.");
+ }
+ } catch (SQLException ex) {
+ ex.printStackTrace();
}
- } catch (SQLException ex) {
- ex.printStackTrace();
+ } finally {
+ c.releaseClient();
}
- } finally {
- c.unlockClient();
}
}
}
diff --git a/src/client/processor/MakerProcessor.java b/src/client/processor/MakerProcessor.java
index 50b9325b54..44ad0e74dd 100644
--- a/src/client/processor/MakerProcessor.java
+++ b/src/client/processor/MakerProcessor.java
@@ -26,7 +26,6 @@ import client.inventory.MapleInventoryType;
import constants.ItemConstants;
import constants.ServerConstants;
import client.inventory.manipulator.MapleInventoryManipulator;
-import constants.EquipType;
import constants.GameConstants;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -47,168 +46,169 @@ public class MakerProcessor {
private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
public static void makerAction(SeekableLittleEndianAccessor slea, MapleClient c) {
- c.lockClient();
- try {
- int type = slea.readInt();
- int toCreate = slea.readInt();
- int toDisassemble = -1, pos = -1;
- boolean makerSucceeded = true;
+ if (c.tryacquireClient()) {
+ try {
+ int type = slea.readInt();
+ int toCreate = slea.readInt();
+ int toDisassemble = -1, pos = -1;
+ boolean makerSucceeded = true;
- MakerItemFactory.MakerItemCreateEntry recipe;
- Map reagentids = new LinkedHashMap<>();
- int stimulantid = -1;
+ MakerItemFactory.MakerItemCreateEntry recipe;
+ Map reagentids = new LinkedHashMap<>();
+ int stimulantid = -1;
- if(type == 3) { // building monster crystal
- int fromLeftover = toCreate;
- toCreate = ii.getMakerCrystalFromLeftover(toCreate);
- if(toCreate == -1) {
- c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal conversion."));
- return;
- }
+ if(type == 3) { // building monster crystal
+ int fromLeftover = toCreate;
+ toCreate = ii.getMakerCrystalFromLeftover(toCreate);
+ if(toCreate == -1) {
+ c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal conversion."));
+ return;
+ }
- recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover);
- } else if(type == 4) { // disassembling
- slea.readInt(); // 1... probably inventory type
- pos = slea.readInt();
+ recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover);
+ } else if(type == 4) { // disassembling
+ slea.readInt(); // 1... probably inventory type
+ pos = slea.readInt();
- Item it = c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem((short) pos);
- if(it != null && it.getItemId() == toCreate) {
- Pair p;
+ Item it = c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem((short) pos);
+ if(it != null && it.getItemId() == toCreate) {
+ Pair p;
- if((p = generateDisassemblyInfo(toCreate)) != null) {
- recipe = MakerItemFactory.generateDisassemblyCrystalEntry(p.getLeft(), p.getRight());
- toDisassemble = toCreate;
- toCreate = ii.getMakerCrystalFromEquip(toCreate);
+ if((p = generateDisassemblyInfo(toCreate)) != null) {
+ recipe = MakerItemFactory.generateDisassemblyCrystalEntry(p.getLeft(), p.getRight());
+ toDisassemble = toCreate;
+ toCreate = ii.getMakerCrystalFromEquip(toCreate);
+ } else {
+ c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal disassembly."));
+ return;
+ }
} else {
- c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal disassembly."));
+ c.announce(MaplePacketCreator.serverNotice(1, "An unknown error occurred when trying to apply that item for disassembly."));
return;
}
} else {
- c.announce(MaplePacketCreator.serverNotice(1, "An unknown error occurred when trying to apply that item for disassembly."));
- return;
- }
- } else {
- if(ItemConstants.isEquipment(toCreate)) { // only equips uses stimulant and reagents
- if(slea.readByte() != 0) { // stimulant
- stimulantid = ii.getMakerStimulant(toCreate);
- if(!c.getAbstractPlayerInteraction().haveItem(stimulantid)) {
- stimulantid = -1;
- }
- }
-
- int reagents = Math.min(slea.readInt(), getMakerReagentSlots(toCreate));
- for(int i = 0; i < reagents; i++) { // crystals
- int reagentid = slea.readInt();
- if(ItemConstants.isMakerReagent(reagentid)) {
- Short rs = reagentids.get(reagentid);
- if(rs == null) {
- reagentids.put(reagentid, (short) 1);
- } else {
- reagentids.put(reagentid, (short) (rs + 1));
+ if(ItemConstants.isEquipment(toCreate)) { // only equips uses stimulant and reagents
+ if(slea.readByte() != 0) { // stimulant
+ stimulantid = ii.getMakerStimulant(toCreate);
+ if(!c.getAbstractPlayerInteraction().haveItem(stimulantid)) {
+ stimulantid = -1;
}
}
- }
- List> toUpdate = new LinkedList<>();
- for(Map.Entry r : reagentids.entrySet()) {
- int qty = c.getAbstractPlayerInteraction().getItemQuantity(r.getKey());
-
- if(qty < r.getValue()) {
- toUpdate.add(new Pair<>(r.getKey(), (short) qty));
- }
- }
-
- // remove those not present on player inventory
- if(!toUpdate.isEmpty()) {
- for(Pair rp : toUpdate) {
- if(rp.getRight() > 0) {
- reagentids.put(rp.getLeft(), rp.getRight());
- } else {
- reagentids.remove(rp.getLeft());
- }
- }
- }
-
- if(!reagentids.isEmpty()) {
- if(!removeOddMakerReagents(toCreate, reagentids)) {
- c.announce(MaplePacketCreator.serverNotice(1, "You can only use WATK and MATK Strengthening Gems on weapon items."));
- return;
- }
- }
- }
-
- recipe = MakerItemFactory.getItemCreateEntry(toCreate, stimulantid, reagentids);
- }
-
- short createStatus = getCreateStatus(c, recipe);
-
- switch(createStatus) {
- case -1:// non-available for Maker itemid has been tried to forge
- FilePrinter.printError(FilePrinter.EXPLOITS, "Player " + c.getPlayer().getName() + " tried to craft itemid " + toCreate + " using the Maker skill.");
- c.announce(MaplePacketCreator.serverNotice(1, "The requested item could not be crafted on this operation."));
- break;
-
- case 1: // no items
- c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + "."));
- break;
-
- case 2: // no meso
- c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough mesos (" + GameConstants.numberWithCommas(recipe.getCost()) + ") to complete this operation."));
- break;
-
- case 3: // no req level
- c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough level to complete this operation."));
- break;
-
- case 4: // no req skill level
- c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough Maker level to complete this operation."));
- break;
-
- default:
- if (MapleInventoryManipulator.checkSpace(c, toCreate, (short) recipe.getRewardAmount(), "")) {
- for (Pair p : recipe.getReqItems()) {
- c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight());
- }
-
- if(toDisassemble != -1) {
- MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false);
- c.announce(MaplePacketCreator.getShowItemGain(toDisassemble, (short) -1, true));
- }
-
- int cost = recipe.getCost();
- if(stimulantid == -1 && reagentids.isEmpty()) {
- if(cost > 0) c.getPlayer().gainMeso(-cost);
-
- c.getPlayer().setCS(true);
- c.getAbstractPlayerInteraction().gainItem(toCreate, (short) recipe.getRewardAmount());
- c.getPlayer().setCS(false);
- } else {
- if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1);
- if(!reagentids.isEmpty()) {
- for(Map.Entry r : reagentids.entrySet()) {
- c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue()));
+ int reagents = Math.min(slea.readInt(), getMakerReagentSlots(toCreate));
+ for(int i = 0; i < reagents; i++) { // crystals
+ int reagentid = slea.readInt();
+ if(ItemConstants.isMakerReagent(reagentid)) {
+ Short rs = reagentids.get(reagentid);
+ if(rs == null) {
+ reagentids.put(reagentid, (short) 1);
+ } else {
+ reagentids.put(reagentid, (short) (rs + 1));
}
}
-
- if(cost > 0) c.getPlayer().gainMeso(-cost);
- makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids);
}
- if(makerSucceeded) c.announce(MaplePacketCreator.serverNotice(1, "You have successfully created " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + "."));
- else c.getPlayer().dropMessage(5, "The Maker skill lights up, but the skill winds up as if nothing happened.");
+ List> toUpdate = new LinkedList<>();
+ for(Map.Entry r : reagentids.entrySet()) {
+ int qty = c.getAbstractPlayerInteraction().getItemQuantity(r.getKey());
- c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded));
- c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false);
-
- if(toCreate == 4260003 && c.getPlayer().getQuestStatus(6033) == 1) {
- c.getAbstractPlayerInteraction().setQuestProgress(6033, 1);
+ if(qty < r.getValue()) {
+ toUpdate.add(new Pair<>(r.getKey(), (short) qty));
+ }
+ }
+
+ // remove those not present on player inventory
+ if(!toUpdate.isEmpty()) {
+ for(Pair rp : toUpdate) {
+ if(rp.getRight() > 0) {
+ reagentids.put(rp.getLeft(), rp.getRight());
+ } else {
+ reagentids.remove(rp.getLeft());
+ }
+ }
+ }
+
+ if(!reagentids.isEmpty()) {
+ if(!removeOddMakerReagents(toCreate, reagentids)) {
+ c.announce(MaplePacketCreator.serverNotice(1, "You can only use WATK and MATK Strengthening Gems on weapon items."));
+ return;
+ }
}
- } else {
- c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full."));
}
+
+ recipe = MakerItemFactory.getItemCreateEntry(toCreate, stimulantid, reagentids);
+ }
+
+ short createStatus = getCreateStatus(c, recipe);
+
+ switch(createStatus) {
+ case -1:// non-available for Maker itemid has been tried to forge
+ FilePrinter.printError(FilePrinter.EXPLOITS, "Player " + c.getPlayer().getName() + " tried to craft itemid " + toCreate + " using the Maker skill.");
+ c.announce(MaplePacketCreator.serverNotice(1, "The requested item could not be crafted on this operation."));
+ break;
+
+ case 1: // no items
+ c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + "."));
+ break;
+
+ case 2: // no meso
+ c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough mesos (" + GameConstants.numberWithCommas(recipe.getCost()) + ") to complete this operation."));
+ break;
+
+ case 3: // no req level
+ c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough level to complete this operation."));
+ break;
+
+ case 4: // no req skill level
+ c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough Maker level to complete this operation."));
+ break;
+
+ default:
+ if (MapleInventoryManipulator.checkSpace(c, toCreate, (short) recipe.getRewardAmount(), "")) {
+ for (Pair p : recipe.getReqItems()) {
+ c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight());
+ }
+
+ if(toDisassemble != -1) {
+ MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false);
+ c.announce(MaplePacketCreator.getShowItemGain(toDisassemble, (short) -1, true));
+ }
+
+ int cost = recipe.getCost();
+ if(stimulantid == -1 && reagentids.isEmpty()) {
+ if(cost > 0) c.getPlayer().gainMeso(-cost);
+
+ c.getPlayer().setCS(true);
+ c.getAbstractPlayerInteraction().gainItem(toCreate, (short) recipe.getRewardAmount());
+ c.getPlayer().setCS(false);
+ } else {
+ if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1);
+ if(!reagentids.isEmpty()) {
+ for(Map.Entry r : reagentids.entrySet()) {
+ c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue()));
+ }
+ }
+
+ if(cost > 0) c.getPlayer().gainMeso(-cost);
+ makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids);
+ }
+
+ if(makerSucceeded) c.announce(MaplePacketCreator.serverNotice(1, "You have successfully created " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + "."));
+ else c.getPlayer().dropMessage(5, "The Maker skill lights up, but the skill winds up as if nothing happened.");
+
+ c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded));
+ c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false);
+
+ if(toCreate == 4260003 && c.getPlayer().getQuestStatus(6033) == 1) {
+ c.getAbstractPlayerInteraction().setQuestProgress(6033, 1);
+ }
+ } else {
+ c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full."));
+ }
+ }
+ } finally {
+ c.releaseClient();
}
- } finally {
- c.unlockClient();
}
}
diff --git a/src/client/processor/SpawnPetProcessor.java b/src/client/processor/SpawnPetProcessor.java
index c45067106c..997af5acec 100644
--- a/src/client/processor/SpawnPetProcessor.java
+++ b/src/client/processor/SpawnPetProcessor.java
@@ -40,60 +40,61 @@ public class SpawnPetProcessor {
private static MapleDataProvider dataRoot = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
public static void processSpawnPet(MapleClient c, byte slot, boolean lead) {
- c.lockClient();
- try {
- MapleCharacter chr = c.getPlayer();
- MaplePet pet = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getPet();
- if (pet == null) return;
+ if (c.tryacquireClient()) {
+ try {
+ MapleCharacter chr = c.getPlayer();
+ MaplePet pet = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getPet();
+ if (pet == null) return;
- int petid = pet.getItemId();
- if (petid == 5000028 || petid == 5000047) //Handles Dragon AND Robos
- {
- if (chr.haveItem(petid + 1)) {
- chr.dropMessage(5, "You can't hatch your " + (petid == 5000028 ? "Dragon egg" : "Robo egg") + " if you already have a Baby " + (petid == 5000028 ? "Dragon." : "Robo."));
- c.announce(MaplePacketCreator.enableActions());
- return;
- } else {
- int evolveid = MapleDataTool.getInt("info/evol1", dataRoot.getData("Pet/" + petid + ".img"));
- int petId = MaplePet.createPet(evolveid);
- if (petId == -1) {
+ int petid = pet.getItemId();
+ if (petid == 5000028 || petid == 5000047) //Handles Dragon AND Robos
+ {
+ if (chr.haveItem(petid + 1)) {
+ chr.dropMessage(5, "You can't hatch your " + (petid == 5000028 ? "Dragon egg" : "Robo egg") + " if you already have a Baby " + (petid == 5000028 ? "Dragon." : "Robo."));
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ } else {
+ int evolveid = MapleDataTool.getInt("info/evol1", dataRoot.getData("Pet/" + petid + ".img"));
+ int petId = MaplePet.createPet(evolveid);
+ if (petId == -1) {
+ return;
+ }
+ long expiration = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getExpiration();
+ MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, petid, (short) 1, false, false);
+ MapleInventoryManipulator.addById(c, evolveid, (short) 1, null, petId, expiration);
+ pet.deleteFromDb();
+
+ c.announce(MaplePacketCreator.enableActions());
return;
}
- long expiration = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getExpiration();
- MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, petid, (short) 1, false, false);
- MapleInventoryManipulator.addById(c, evolveid, (short) 1, null, petId, expiration);
- pet.deleteFromDb();
-
+ }
+ if (chr.getPetIndex(pet) != -1) {
+ chr.unequipPet(pet, true);
+ } else {
+ if (chr.getSkillLevel(SkillFactory.getSkill(8)) == 0 && chr.getPet(0) != null) {
+ chr.unequipPet(chr.getPet(0), false);
+ }
+ if (lead) {
+ chr.shiftPetsRight();
+ }
+ Point pos = chr.getPosition();
+ pos.y -= 12;
+ pet.setPos(pos);
+ pet.setFh(chr.getMap().getFootholds().findBelow(pet.getPos()).getId());
+ pet.setStance(0);
+ pet.setSummoned(true);
+ pet.saveToDb();
+ chr.addPet(pet);
+ chr.getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showPet(c.getPlayer(), pet, false, false), true);
+ c.announce(MaplePacketCreator.petStatUpdate(c.getPlayer()));
c.announce(MaplePacketCreator.enableActions());
- return;
- }
- }
- if (chr.getPetIndex(pet) != -1) {
- chr.unequipPet(pet, true);
- } else {
- if (chr.getSkillLevel(SkillFactory.getSkill(8)) == 0 && chr.getPet(0) != null) {
- chr.unequipPet(chr.getPet(0), false);
- }
- if (lead) {
- chr.shiftPetsRight();
- }
- Point pos = chr.getPosition();
- pos.y -= 12;
- pet.setPos(pos);
- pet.setFh(chr.getMap().getFootholds().findBelow(pet.getPos()).getId());
- pet.setStance(0);
- pet.setSummoned(true);
- pet.saveToDb();
- chr.addPet(pet);
- chr.getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showPet(c.getPlayer(), pet, false, false), true);
- c.announce(MaplePacketCreator.petStatUpdate(c.getPlayer()));
- c.announce(MaplePacketCreator.enableActions());
- chr.commitExcludedItems();
- chr.getClient().getWorldServer().registerPetHunger(chr, chr.getPetIndex(pet));
+ chr.commitExcludedItems();
+ chr.getClient().getWorldServer().registerPetHunger(chr, chr.getPetIndex(pet));
+ }
+ } finally {
+ c.releaseClient();
}
- } finally {
- c.unlockClient();
}
}
}
diff --git a/src/client/processor/StorageProcessor.java b/src/client/processor/StorageProcessor.java
index c722a47c6d..386006d2f6 100644
--- a/src/client/processor/StorageProcessor.java
+++ b/src/client/processor/StorageProcessor.java
@@ -54,123 +54,124 @@ public class StorageProcessor {
return;
}
- c.lockClient();
- try {
- if (mode == 4) { // take out
- byte type = slea.readByte();
- byte slot = slea.readByte();
- if (slot < 0 || slot > storage.getSlots()) { // removal starts at zero
- AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with storage.");
- FilePrinter.print(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to work with storage slot " + slot + "\r\n");
- c.disconnect(true, false);
- return;
- }
- slot = storage.getSlot(MapleInventoryType.getByType(type), slot);
- Item item = storage.getItem(slot);
- if (item != null) {
- if (MapleItemInformationProvider.getInstance().isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
- c.announce(MaplePacketCreator.getStorageError((byte) 0x0C));
+ if (c.tryacquireClient()) {
+ try {
+ if (mode == 4) { // take out
+ byte type = slea.readByte();
+ byte slot = slea.readByte();
+ if (slot < 0 || slot > storage.getSlots()) { // removal starts at zero
+ AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with storage.");
+ FilePrinter.print(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to work with storage slot " + slot + "\r\n");
+ c.disconnect(true, false);
return;
}
-
- int takeoutFee = storage.getTakeOutFee();
- if (chr.getMeso() < takeoutFee) {
+ slot = storage.getSlot(MapleInventoryType.getByType(type), slot);
+ Item item = storage.getItem(slot);
+ if (item != null) {
+ if (MapleItemInformationProvider.getInstance().isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
+ c.announce(MaplePacketCreator.getStorageError((byte) 0x0C));
+ return;
+ }
+
+ int takeoutFee = storage.getTakeOutFee();
+ if (chr.getMeso() < takeoutFee) {
+ c.announce(MaplePacketCreator.getStorageError((byte) 0x0B));
+ return;
+ } else {
+ chr.gainMeso(-takeoutFee, false);
+ }
+
+ if (MapleInventoryManipulator.checkSpace(c, item.getItemId(), item.getQuantity(), item.getOwner())) {
+ item = storage.takeOut(slot);//actually the same but idc
+ String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
+ FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
+ chr.setUsedStorage();
+ MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
+ MapleInventoryManipulator.addFromDrop(c, item, false);
+ storage.sendTakenOut(c, item.getInventoryType());
+ } else {
+ c.announce(MaplePacketCreator.getStorageError((byte) 0x0A));
+ }
+ }
+ } else if (mode == 5) { // store
+ short slot = slea.readShort();
+ int itemId = slea.readInt();
+ short quantity = slea.readShort();
+ MapleInventoryType slotType = ItemConstants.getInventoryType(itemId);
+ MapleInventory Inv = chr.getInventory(slotType);
+ if (slot < 1 || slot > Inv.getSlotLimit()) { //player inv starts at one
+ AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with storage.");
+ FilePrinter.print(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to store item at slot " + slot + "\r\n");
+ c.disconnect(true, false);
+ return;
+ }
+ if (quantity < 1 || chr.getItemQuantity(itemId, false) < quantity) {
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ }
+ if (storage.isFull()) {
+ c.announce(MaplePacketCreator.getStorageError((byte) 0x11));
+ return;
+ }
+
+ int storeFee = storage.getStoreFee();
+ if (chr.getMeso() < storeFee) {
c.announce(MaplePacketCreator.getStorageError((byte) 0x0B));
+ } else {
+ MapleInventoryType invType = ItemConstants.getInventoryType(itemId);
+ Item item = chr.getInventory(invType).getItem(slot).copy();
+ if (item != null && item.getItemId() == itemId && (item.getQuantity() >= quantity || ItemConstants.isRechargeable(itemId))) {
+ if (ItemConstants.isRechargeable(itemId)) {
+ quantity = item.getQuantity();
+ }
+
+ chr.gainMeso(-storeFee, false, true, false);
+ MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
+ MapleInventoryManipulator.removeFromSlot(c, invType, slot, quantity, false);
+ item.setQuantity(quantity);
+ storage.store(item);
+ storage.sendStored(c, ItemConstants.getInventoryType(itemId));
+ String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
+ FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
+ chr.setUsedStorage();
+ }
+ }
+ } else if (mode == 6) { // arrange items
+ if(ServerConstants.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c);
+ c.announce(MaplePacketCreator.enableActions());
+ } else if (mode == 7) { // meso
+ int meso = slea.readInt();
+ int storageMesos = storage.getMeso();
+ int playerMesos = chr.getMeso();
+ if ((meso > 0 && storageMesos >= meso) || (meso < 0 && playerMesos >= -meso)) {
+ if (meso < 0 && (storageMesos - meso) < 0) {
+ meso = Integer.MIN_VALUE + storageMesos;
+ if (meso < playerMesos) {
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ }
+ } else if (meso > 0 && (playerMesos + meso) < 0) {
+ meso = Integer.MAX_VALUE - playerMesos;
+ if (meso > storageMesos) {
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ }
+ }
+ storage.setMeso(storageMesos - meso);
+ chr.gainMeso(meso, false, true, false);
+ FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos\r\n");
+ chr.setUsedStorage();
+ } else {
+ c.announce(MaplePacketCreator.enableActions());
return;
- } else {
- chr.gainMeso(-takeoutFee, false);
- }
-
- if (MapleInventoryManipulator.checkSpace(c, item.getItemId(), item.getQuantity(), item.getOwner())) {
- item = storage.takeOut(slot);//actually the same but idc
- String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
- FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
- chr.setUsedStorage();
- MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
- MapleInventoryManipulator.addFromDrop(c, item, false);
- storage.sendTakenOut(c, item.getInventoryType());
- } else {
- c.announce(MaplePacketCreator.getStorageError((byte) 0x0A));
}
+ storage.sendMeso(c);
+ } else if (mode == 8) {// close
+ storage.close();
}
- } else if (mode == 5) { // store
- short slot = slea.readShort();
- int itemId = slea.readInt();
- short quantity = slea.readShort();
- MapleInventoryType slotType = ItemConstants.getInventoryType(itemId);
- MapleInventory Inv = chr.getInventory(slotType);
- if (slot < 1 || slot > Inv.getSlotLimit()) { //player inv starts at one
- AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with storage.");
- FilePrinter.print(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to store item at slot " + slot + "\r\n");
- c.disconnect(true, false);
- return;
- }
- if (quantity < 1 || chr.getItemQuantity(itemId, false) < quantity) {
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- if (storage.isFull()) {
- c.announce(MaplePacketCreator.getStorageError((byte) 0x11));
- return;
- }
-
- int storeFee = storage.getStoreFee();
- if (chr.getMeso() < storeFee) {
- c.announce(MaplePacketCreator.getStorageError((byte) 0x0B));
- } else {
- MapleInventoryType invType = ItemConstants.getInventoryType(itemId);
- Item item = chr.getInventory(invType).getItem(slot).copy();
- if (item != null && item.getItemId() == itemId && (item.getQuantity() >= quantity || ItemConstants.isRechargeable(itemId))) {
- if (ItemConstants.isRechargeable(itemId)) {
- quantity = item.getQuantity();
- }
-
- chr.gainMeso(-storeFee, false, true, false);
- MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
- MapleInventoryManipulator.removeFromSlot(c, invType, slot, quantity, false);
- item.setQuantity(quantity);
- storage.store(item);
- storage.sendStored(c, ItemConstants.getInventoryType(itemId));
- String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
- FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")\r\n");
- chr.setUsedStorage();
- }
- }
- } else if (mode == 6) { // arrange items
- if(ServerConstants.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c);
- c.announce(MaplePacketCreator.enableActions());
- } else if (mode == 7) { // meso
- int meso = slea.readInt();
- int storageMesos = storage.getMeso();
- int playerMesos = chr.getMeso();
- if ((meso > 0 && storageMesos >= meso) || (meso < 0 && playerMesos >= -meso)) {
- if (meso < 0 && (storageMesos - meso) < 0) {
- meso = -2147483648 + storageMesos;
- if (meso < playerMesos) {
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- } else if (meso > 0 && (playerMesos + meso) < 0) {
- meso = 2147483647 - playerMesos;
- if (meso > storageMesos) {
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- }
- storage.setMeso(storageMesos - meso);
- chr.gainMeso(meso, false, true, false);
- FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos\r\n");
- chr.setUsedStorage();
- } else {
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- storage.sendMeso(c);
- } else if (mode == 8) {// close
- storage.close();
+ } finally {
+ c.releaseClient();
}
- } finally {
- c.unlockClient();
}
}
}
diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java
index 03c69079cb..0d445deb39 100644
--- a/src/constants/ServerConstants.java
+++ b/src/constants/ServerConstants.java
@@ -54,6 +54,7 @@ public class ServerConstants {
public static final boolean USE_DEBUG = false; //Will enable some text prints on the client, oriented for debugging purposes.
public static final boolean USE_DEBUG_SHOW_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info.
public static boolean USE_DEBUG_SHOW_RCVD_PACKET = false; //Prints on the cmd all received packet ids.
+ public static boolean USE_SUPPLY_RATE_COUPONS = true; //Allows rate coupons to be sold through the Cash Shop.
public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events on a map, rather than those of only view range.
public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true;
@@ -104,6 +105,7 @@ public class ServerConstants {
//Cash Shop Configuration
public static final boolean USE_JOINT_CASHSHOP_INVENTORY = true;//Enables usage of a same cash shop inventory for explorers, cygnus and legends. Items from exclusive cash shop inventories won't show up on the shared inventory, though.
+ public static final boolean USE_CLEAR_OUTDATED_COUPONS = true; //Enables deletion of older code coupon registry from the DB, freeing so-long irrelevant data.
//Maker Configuration
public static final boolean USE_MAKER_PERMISSIVE_ATKUP = true; //Allows players to use attack-based strengthening gems on non-weapon items.
@@ -148,7 +150,8 @@ public class ServerConstants {
//Map Monitor Configuration
public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items.
public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map.
- public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions.
+ public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions.
+ public static final int MAP_DAMAGE_OVERTIME_INTERVAL = 5000;//Interval in seconds between map environment damage (e.g. El Nath and Aqua Road surrondings).
//Channel Mob Disease Monitor Configuration
public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule.
@@ -172,7 +175,7 @@ public class ServerConstants {
//Other Skills Configuration
public static final boolean USE_FAST_REUSE_HERO_WILL = true;//Greatly reduce cooldown on Hero's Will.
- public static final boolean USE_ANTI_IMMUNITY_CRASH = true; //Crash skills additionally removes the mob's invincibility buffs.
+ public static final boolean USE_ANTI_IMMUNITY_CRASH = true; //Crash skills additionally removes the mob's invincibility buffs. Suggestion thanks to Celestial.
public static final boolean USE_UNDISPEL_HOLY_SHIELD = true;//Holy shield buff also prevents players from suffering dispel from mobs.
//Character Configuration
@@ -200,6 +203,7 @@ public class ServerConstants {
//Equipment Configuration
public static final boolean USE_EQUIPMNT_LVLUP_SLOTS = true;//Equips can upgrade slots at level up.
public static final boolean USE_EQUIPMNT_LVLUP_POWER = true;//Enable more powerful stat upgrades at equip level up.
+ public static final boolean USE_EQUIPMNT_LVLUP_CASH = true; //Enable equip leveling up on cash equipments as well.
public static final boolean USE_SPIKES_AVOID_BANISH = true; //Shoes equipped with spikes prevents mobs from banishing wearer.
public static final int MAX_EQUIPMNT_LVLUP_STAT_UP = 10000; //Max stat upgrade an equipment can have on a levelup.
public static final int MAX_EQUIPMNT_STAT = 32767; //Max stat on an equipment by leveling up.
diff --git a/src/constants/skills/SuperGM.java b/src/constants/skills/SuperGM.java
index 17e7ccb77a..e6f9dbee3a 100644
--- a/src/constants/skills/SuperGM.java
+++ b/src/constants/skills/SuperGM.java
@@ -33,6 +33,5 @@ public class SuperGM {
public static final int HIDE = 9101004;
public static final int RESURRECTION = 9101005;
public static final int SUPER_DRAGON_ROAR = 9001001;
- public static final int TELEPORT = 9101007;
public static final int HYPER_BODY = 9101008;
}
\ No newline at end of file
diff --git a/src/net/server/Server.java b/src/net/server/Server.java
index 58a16a8649..42b3561d31 100644
--- a/src/net/server/Server.java
+++ b/src/net/server/Server.java
@@ -80,6 +80,7 @@ import client.MapleCharacter;
import client.SkillFactory;
import client.inventory.Item;
import client.inventory.ItemFactory;
+import client.inventory.manipulator.MapleCashidGenerator;
import client.newyear.NewYearCardRecord;
import constants.ItemConstants;
import constants.GameConstants;
@@ -90,7 +91,6 @@ import server.life.MaplePlayerNPCFactory;
import server.quest.MapleQuest;
import tools.AutoJCE;
import tools.DatabaseConnection;
-import tools.FilePrinter;
import tools.Pair;
public class Server {
@@ -518,6 +518,34 @@ public class Server {
return couponRates;
}
+ public static void cleanNxcodeCoupons(Connection con) throws SQLException {
+ if (!ServerConstants.USE_CLEAR_OUTDATED_COUPONS) return;
+
+ long timeClear = System.currentTimeMillis() - 14 * 24 * 60 * 60 * 1000;
+
+ PreparedStatement ps = con.prepareStatement("SELECT * FROM nxcode WHERE expiration <= ?");
+ ps.setLong(1, timeClear);
+ ResultSet rs = ps.executeQuery();
+
+ if (!rs.isLast()) {
+ PreparedStatement ps2 = con.prepareStatement("DELETE FROM nxcode_items WHERE codeid = ?");
+ while (rs.next()) {
+ ps2.setInt(1, rs.getInt("id"));
+ ps2.addBatch();
+ }
+ ps2.executeBatch();
+ ps2.close();
+
+ ps2 = con.prepareStatement("DELETE FROM nxcode WHERE expiration <= ?");
+ ps2.setLong(1, timeClear);
+ ps2.executeUpdate();
+ ps2.close();
+ }
+
+ rs.close();
+ ps.close();
+ }
+
private void loadCouponRates(Connection c) throws SQLException {
PreparedStatement ps = c.prepareStatement("SELECT couponid, rate FROM nxcoupons");
ResultSet rs = ps.executeQuery();
@@ -818,6 +846,7 @@ public class Server {
ps.executeUpdate();
ps.close();
+ cleanNxcodeCoupons(c);
loadCouponRates(c);
updateActiveCoupons();
@@ -825,6 +854,9 @@ public class Server {
} catch (SQLException sqle) {
sqle.printStackTrace();
}
+
+ MapleCashidGenerator.loadExistentCashIdsFromDb();
+
IoBuffer.setUseDirectBuffer(false);
IoBuffer.setAllocator(new SimpleBufferAllocator());
acceptor = new NioSocketAcceptor();
@@ -893,17 +925,6 @@ public class Server {
online = true;
}
- public void shutdown() {
- try {
- TimerManager.getInstance().stop();
- acceptor.unbind();
- } catch (NullPointerException e) {
- FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, e);
- }
- System.out.println("Server offline.");
- System.exit(0);// BOEIEND :D
- }
-
public static void main(String args[]) {
System.setProperty("wzpath", "wz");
Security.setProperty("crypto.policy", "unlimited");
diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java
index 5285ec2939..02f08c9a68 100644
--- a/src/net/server/channel/Channel.java
+++ b/src/net/server/channel/Channel.java
@@ -198,6 +198,7 @@ public final class Channel {
System.out.println("Shutting down Channel " + channel + " on World " + world);
closeAllMerchants();
+ disconnectAwayPlayers();
players.disconnectAll();
if(respawnTask != null) {
@@ -314,6 +315,10 @@ public final class Channel {
return world;
}
+ public World getWorldServer() {
+ return Server.getInstance().getWorld(world);
+ }
+
public void addPlayer(MapleCharacter chr) {
players.addPlayer(chr);
chr.announce(MaplePacketCreator.serverMessage(serverMessage));
@@ -393,6 +398,16 @@ public final class Channel {
public boolean canUninstall() {
return players.getSize() == 0 && playersAway.isEmpty();
}
+
+ private void disconnectAwayPlayers() {
+ World wserv = getWorldServer();
+ for (Integer cid : playersAway) {
+ MapleCharacter chr = wserv.getPlayerStorage().getCharacterById(cid);
+ if (chr != null && chr.isLoggedin()) {
+ chr.getClient().disconnect(true, false);
+ }
+ }
+ }
public class respawnMaps implements Runnable {
@@ -465,7 +480,7 @@ public final class Channel {
public void setServerMessage(String message) {
this.serverMessage = message;
broadcastPacket(MaplePacketCreator.serverMessage(message));
- Server.getInstance().getWorld(world).resetDisabledServerMessages();
+ getWorldServer().resetDisabledServerMessages();
}
private static String [] getEvents(){
@@ -692,7 +707,7 @@ public final class Channel {
lock.unlock();
}
- World wserv = Server.getInstance().getWorld(world);
+ World wserv = getWorldServer();
Pair coupleId = wserv.getMarriageQueuedCouple(ret);
Pair> typeGuests = wserv.removeMarriageQueued(ret);
@@ -704,7 +719,7 @@ public final class Channel {
}
public boolean isWeddingReserved(Integer weddingId) {
- World wserv = Server.getInstance().getWorld(world);
+ World wserv = getWorldServer();
lock.lock();
try {
@@ -746,7 +761,7 @@ public final class Channel {
public int pushWeddingReservation(Integer weddingId, boolean cathedral, boolean premium, Integer groomId, Integer brideId) {
if(weddingId == null || isWeddingReserved(weddingId)) return -1;
- World wserv = Server.getInstance().getWorld(world);
+ World wserv = getWorldServer();
wserv.putMarriageQueued(weddingId, cathedral, premium, groomId, brideId);
lock.lock();
@@ -935,7 +950,7 @@ public final class Channel {
public Pair getWeddingCoupleForGuest(int guestId, boolean cathedral) {
lock.lock();
try {
- return (isOngoingWeddingGuest(cathedral, guestId)) ? Server.getInstance().getWorld(world).getRelationshipCouple(getOngoingWedding(cathedral)) : null;
+ return (isOngoingWeddingGuest(cathedral, guestId)) ? getWorldServer().getRelationshipCouple(getOngoingWedding(cathedral)) : null;
} finally {
lock.unlock();
}
@@ -1038,7 +1053,7 @@ public final class Channel {
public void debugMarriageStatus() {
System.out.println(" ----- WORLD DATA -----");
- Server.getInstance().getWorld(world).debugMarriageStatus();
+ getWorldServer().debugMarriageStatus();
System.out.println(" ----- CH. " + channel + " -----");
System.out.println(" ----- CATHEDRAL -----");
diff --git a/src/net/server/channel/handlers/CancelChairHandler.java b/src/net/server/channel/handlers/CancelChairHandler.java
index e226841e13..0b6693be51 100644
--- a/src/net/server/channel/handlers/CancelChairHandler.java
+++ b/src/net/server/channel/handlers/CancelChairHandler.java
@@ -24,7 +24,6 @@ package net.server.channel.handlers;
import client.MapleClient;
import client.MapleCharacter;
import net.AbstractMaplePacketHandler;
-import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class CancelChairHandler extends AbstractMaplePacketHandler {
@@ -35,21 +34,6 @@ public final class CancelChairHandler extends AbstractMaplePacketHandler {
MapleCharacter mc = c.getPlayer();
if(!mc.isLoggedinWorld()) return;
- if (id == -1) { // Cancel Chair
- mc.setChair(0);
- if(mc.unregisterChairBuff()) {
- c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.cancelForeignChairSkillEffect(mc.getId()), false);
- }
-
- c.announce(MaplePacketCreator.cancelChair(-1));
- c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showChair(c.getPlayer().getId(), 0), false);
- } else { // Use In-Map Chair
- mc.setChair(id);
- if(mc.registerChairBuff()) {
- c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.giveForeignChairSkillEffect(mc.getId()), false);
- }
-
- c.announce(MaplePacketCreator.cancelChair(id));
- }
+ mc.sitChair(id == -1 ? 0 : id);
}
}
diff --git a/src/net/server/channel/handlers/CashOperationHandler.java b/src/net/server/channel/handlers/CashOperationHandler.java
index a44097c85a..2e2659266e 100644
--- a/src/net/server/channel/handlers/CashOperationHandler.java
+++ b/src/net/server/channel/handlers/CashOperationHandler.java
@@ -38,8 +38,10 @@ import server.CashShop;
import server.CashShop.CashItem;
import server.CashShop.CashItemFactory;
import client.inventory.manipulator.MapleInventoryManipulator;
+import constants.ServerConstants;
import tools.FilePrinter;
import tools.MaplePacketCreator;
+import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
public final class CashOperationHandler extends AbstractMaplePacketHandler {
@@ -62,18 +64,20 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
CashItem cItem = CashItemFactory.getItem(snCS);
if (!canBuy(cItem, cs.getCash(useNX))) {
FilePrinter.printError(FilePrinter.ITEM, "Denied to sell cash item with SN " + cItem.getSN());
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
-
- if(ItemConstants.isCashStore(cItem.getItemId()) && chr.getLevel() < 16) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
if (action == 0x03) { // Item
- if(ItemConstants.isMapleLife(cItem.getItemId()) && chr.getLevel() < 30) {
- c.announce(MaplePacketCreator.enableActions());
+ if (ItemConstants.isCashStore(cItem.getItemId()) && chr.getLevel() < 16) {
+ c.enableCSActions();
+ return;
+ } else if (ItemConstants.isRateCoupon(cItem.getItemId()) && !ServerConstants.USE_SUPPLY_RATE_COUPONS) {
+ chr.dropMessage(1, "Rate coupons are currently unavailable to purchase.");
+ c.enableCSActions();
+ return;
+ } else if (ItemConstants.isMapleLife(cItem.getItemId()) && chr.getLevel() < 30) {
+ c.enableCSActions();
return;
}
@@ -95,20 +99,17 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
Map recipient = MapleCharacter.getCharacterFromDatabase(slea.readMapleAsciiString());
String message = slea.readMapleAsciiString();
if (!canBuy(cItem, cs.getCash(4)) || message.length() < 1 || message.length() > 73) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
if (!checkBirthday(c, birthday)) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
- c.announce(MaplePacketCreator.enableActions());
return;
} else if (recipient == null) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA9));
- c.announce(MaplePacketCreator.enableActions());
return;
} else if (recipient.get("accountid").equals(String.valueOf(c.getAccID()))) {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xA8));
- c.announce(MaplePacketCreator.enableActions());
return;
}
cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN());
@@ -139,7 +140,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
if (mode == 0) {
byte type = slea.readByte();
if (cs.getCash(cash) < 4000) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
if (chr.gainSlots(type, 4, false)) {
@@ -151,7 +152,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
CashItem cItem = CashItemFactory.getItem(slea.readInt());
int type = (cItem.getItemId() - 9110000) / 1000;
if (!canBuy(cItem, cs.getCash(cash))) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
if (chr.gainSlots(type, 8, false)) {
@@ -166,7 +167,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
byte mode = slea.readByte();
if (mode == 0) {
if (cs.getCash(cash) < 4000) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
if (chr.getStorage().gainSlots(4)) {
@@ -181,7 +182,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
CashItem cItem = CashItemFactory.getItem(slea.readInt());
if (!canBuy(cItem, cs.getCash(cash))) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
if (chr.getStorage().gainSlots(8)) {
@@ -196,7 +197,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
CashItem cItem = CashItemFactory.getItem(slea.readInt());
if (!canBuy(cItem, cs.getCash(cash))) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
@@ -206,13 +207,13 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.showCash(chr));
} else {
chr.dropMessage(1, "You have already used up all 12 extra character slots.");
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
} else if (action == 0x0D) { // Take from Cash Inventory
Item item = cs.findByCashId(slea.readInt());
if (item == null) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
if (chr.getInventory(item.getInventoryType()).addItem(item) != -1) {
@@ -240,11 +241,11 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
MapleInventory mi = chr.getInventory(MapleInventoryType.getByType(invType));
Item item = mi.findByCashId(cashId);
if (item == null) {
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
} else if(c.getPlayer().getPetIndex(item.getPetId()) > -1) {
chr.getClient().announce(MaplePacketCreator.serverNotice(1, "You cannot put the pet you currently equip into the Cash Shop inventory."));
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}
cs.addToInventory(item);
@@ -265,19 +266,19 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
/* if (partner.getGender() == chr.getGender()) {
chr.dropMessage("You and your partner are the same gender, please buy a friendship ring.");
- c.announce(MaplePacketCreator.enableActions());
+ c.enableCSActions();
return;
}*/ //Gotta let them faggots marry too, hence why this is commented out <3
if(itemRing.toItem() instanceof Equip) {
Equip eqp = (Equip) itemRing.toItem();
- int ringid = MapleRing.createRing(itemRing.getItemId(), chr, partner);
- eqp.setRingId(ringid);
+ Pair rings = MapleRing.createRing(itemRing.getItemId(), chr, partner);
+ eqp.setRingId(rings.getLeft());
cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
- cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), (ringid + 1));
+ cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
cs.gainCash(toCharge, itemRing, chr.getWorld());
- chr.addCrushRing(MapleRing.loadFromDb(ringid));
+ chr.addCrushRing(MapleRing.loadFromDb(rings.getLeft()));
try {
chr.sendNote(partner.getName(), text, (byte) 1);
} catch (SQLException ex) {
@@ -288,17 +289,33 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
}
} else {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
- c.announce(MaplePacketCreator.enableActions());
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
- } else if (action == 0x20) { // everything is 1 meso...
- int itemId = CashItemFactory.getItem(slea.readInt()).getItemId();
+ } else if (action == 0x20) {
+ int serialNumber = slea.readInt(); // thanks GabrielSin for detecting a potential exploit with 1 meso cash items.
+ if (serialNumber / 10000000 != 8) {
+ c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
+ return;
+ }
- if (chr.getMeso() > 0) {
+ CashItem item = CashItemFactory.getItem(serialNumber);
+ if (item == null || !item.isOnSale()) {
+ c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
+ return;
+ }
+
+ int itemId = item.getItemId();
+ int itemPrice = item.getPrice();
+ if (itemPrice <= 0) {
+ c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC0));
+ return;
+ }
+
+ if (chr.getMeso() >= itemPrice) {
if (chr.canHold(itemId)) {
- chr.gainMeso(-1, false);
- MapleInventoryManipulator.addById(c, itemId, (short) 1);
+ chr.gainMeso(-itemPrice, false);
+ MapleInventoryManipulator.addById(c, itemId, (short) 1, "", -1);
c.announce(MaplePacketCreator.showBoughtQuestItem(itemId));
}
}
@@ -321,13 +338,13 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
// Need to check to make sure its actually an equip and the right SN...
if(itemRing.toItem() instanceof Equip) {
Equip eqp = (Equip) itemRing.toItem();
- int ringid = MapleRing.createRing(itemRing.getItemId(), chr, partner);
- eqp.setRingId(ringid);
+ Pair rings = MapleRing.createRing(itemRing.getItemId(), chr, partner);
+ eqp.setRingId(rings.getLeft());
cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
- cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), (ringid + 1));
+ cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), rings.getRight());
cs.gainCash(payment, -itemRing.getPrice());
- chr.addFriendshipRing(MapleRing.loadFromDb(ringid));
+ chr.addFriendshipRing(MapleRing.loadFromDb(rings.getLeft()));
try {
chr.sendNote(partner.getName(), text, (byte) 1);
} catch (SQLException ex) {
@@ -338,7 +355,6 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
}
} else {
c.announce(MaplePacketCreator.showCashShopMessage((byte) 0xC4));
- c.announce(MaplePacketCreator.enableActions());
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
diff --git a/src/net/server/channel/handlers/CloseRangeDamageHandler.java b/src/net/server/channel/handlers/CloseRangeDamageHandler.java
index d2e46275c6..00dc153da2 100644
--- a/src/net/server/channel/handlers/CloseRangeDamageHandler.java
+++ b/src/net/server/channel/handlers/CloseRangeDamageHandler.java
@@ -50,7 +50,7 @@ public final class CloseRangeDamageHandler extends AbstractDealDamageHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
- chr.setPetLootCd(currentServerTime());
+ //chr.setPetLootCd(currentServerTime());
/*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8);
if(timeElapsed < 300) {
diff --git a/src/net/server/channel/handlers/CouponCodeHandler.java b/src/net/server/channel/handlers/CouponCodeHandler.java
index 30f2757080..f9c9ae03e1 100644
--- a/src/net/server/channel/handlers/CouponCodeHandler.java
+++ b/src/net/server/channel/handlers/CouponCodeHandler.java
@@ -4,6 +4,8 @@
Matthias Butz
Jan Christian Meyer
+ Copyleft (L) 2016 - 2018 RonanLana (HeavenMS)
+
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
@@ -21,107 +23,228 @@
*/
package net.server.channel.handlers;
-import java.sql.SQLException;
import client.MapleClient;
+import client.MapleCharacter;
+import client.inventory.Item;
+import client.inventory.manipulator.MapleInventoryManipulator;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
import net.AbstractMaplePacketHandler;
-import client.inventory.manipulator.MapleInventoryManipulator;
+import net.server.Server;
+import server.CashShop;
+import server.MapleItemInformationProvider;
import tools.DatabaseConnection;
+import tools.FilePrinter;
import tools.MaplePacketCreator;
+import tools.Pair;
import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author Penguins (Acrylic)
+ * @author Ronan (HeavenMS)
*/
public final class CouponCodeHandler extends AbstractMaplePacketHandler {
+
+ private static List>> getNXCodeItems(MapleCharacter chr, Connection con, int codeid) throws SQLException {
+ Map couponItems = new HashMap<>();
+ Map couponPoints = new HashMap<>(5);
+
+ PreparedStatement ps = con.prepareStatement("SELECT * FROM nxcode_items WHERE codeid = ?");
+ ps.setInt(1, codeid);
+
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ int type = rs.getInt("type"), item = rs.getInt("item");
+
+ if (type < 5) {
+ Integer i = couponPoints.get(type);
+ if (i != null) {
+ couponPoints.put(type, i + item);
+ } else {
+ couponPoints.put(type, item);
+ }
+ } else {
+ int quantity = rs.getInt("quantity");
+
+ Integer i = couponItems.get(item);
+ if (i != null) {
+ couponItems.put(item, i + quantity);
+ } else {
+ couponItems.put(item, quantity);
+ }
+ }
+ }
+
+ rs.close();
+ ps.close();
+
+ List>> ret = new LinkedList<>();
+ if (!couponItems.isEmpty()) {
+ for (Entry e : couponItems.entrySet()) {
+ int item = e.getKey(), qty = e.getValue();
+
+ if (MapleItemInformationProvider.getInstance().getName(item) == null) {
+ item = 4000000;
+ qty = 1;
+
+ FilePrinter.printError(FilePrinter.UNHANDLED_EVENT, "Error trying to redeem itemid " + item + " from codeid " + codeid + ".");
+ }
+
+ if (!chr.canHold(item, qty)) {
+ return null;
+ }
+
+ ret.add(new Pair<>(5, new Pair<>(item, qty)));
+ }
+ }
+
+ if (!couponPoints.isEmpty()) {
+ for (Entry e : couponPoints.entrySet()) {
+ ret.add(new Pair<>(e.getKey(), new Pair<>(e.getValue(), 777)));
+ }
+ }
+
+ return ret;
+ }
+
+ private static Pair>>> getNXCodeResult(MapleCharacter chr, String code) {
+ MapleClient c = chr.getClient();
+ List>> ret = new LinkedList<>();
+ try {
+ if (!c.attemptCsCoupon()) {
+ return new Pair<>(-5, null);
+ }
+
+ Connection con = DatabaseConnection.getConnection();
+ PreparedStatement ps = con.prepareStatement("SELECT * FROM nxcode WHERE code = ?");
+ ps.setString(1, code);
+
+ ResultSet rs = ps.executeQuery();
+ if (!rs.next()) {
+ return new Pair<>(-1, null);
+ }
+
+ if (rs.getString("retriever") != null) {
+ return new Pair<>(-2, null);
+ }
+
+ if (rs.getLong("expiration") < Server.getInstance().getCurrentTime()) {
+ return new Pair<>(-3, null);
+ }
+
+ int codeid = rs.getInt("id");
+ rs.close();
+ ps.close();
+
+ ret = getNXCodeItems(chr, con, codeid);
+ if (ret == null) {
+ return new Pair<>(-4, null);
+ }
+
+ ps = con.prepareStatement("UPDATE nxcode SET retriever = ? WHERE code = ?");
+ ps.setString(1, chr.getName());
+ ps.setString(2, code);
+ ps.executeUpdate();
+
+ ps.close();
+ con.close();
+ } catch (SQLException ex) {
+ ex.printStackTrace();
+ }
+
+ c.resetCsCoupon();
+ return new Pair<>(0, ret);
+ }
+
+ private static int parseCouponResult(int res) {
+ switch (res) {
+ case -1:
+ return 0xB0;
+
+ case -2:
+ return 0xB3;
+
+ case -3:
+ return 0xB2;
+
+ case -4:
+ return 0xBB;
+
+ default:
+ return 0xB1;
+ }
+ }
+
+ @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.skip(2);
String code = slea.readMapleAsciiString();
- boolean validcode = false;
- int type = -1;
- int item = -1;
- validcode = getNXCodeValid(code.toUpperCase(), validcode);
- if (validcode) {
- type = getNXCode(code, "type");
- item = getNXCode(code, "item");
- if (type != 5) {
- try {
- Connection con = DatabaseConnection.getConnection();
- PreparedStatement ps = con.prepareStatement("UPDATE nxcode SET `valid` = 0 WHERE code = " + code);
- ps.executeUpdate();
- ps.close();
- ps = con.prepareStatement("UPDATE nxcode SET `user` = ? WHERE code = ?");
- ps.setString(1, c.getPlayer().getName());
- ps.setString(2, code);
- ps.executeUpdate();
- ps.close();
- con.close();
- } catch (SQLException e) {
- e.printStackTrace();
+
+ if (c.tryacquireClient()) {
+ try {
+ Pair>>> codeRes = getNXCodeResult(c.getPlayer(), code.toUpperCase());
+ int type = codeRes.getLeft();
+ if (type < 0) {
+ c.announce(MaplePacketCreator.showCashShopMessage((byte) parseCouponResult(type)));
+ } else {
+ List
- couponPackage = new LinkedList<>();
+
+ for (Pair> p : codeRes.getRight()) {
+ type = p.getLeft();
+ int item = p.getRight().getLeft();
+
+ CashShop cs = c.getPlayer().getCashShop();
+ switch (type) {
+ case 0:
+ case 4:
+ cs.gainCash(1, item); //nxCredit
+ break;
+ case 1:
+ cs.gainCash(2, item); //maplePoint
+ break;
+ case 2:
+ cs.gainCash(4, item); //nxPrepaid
+ break;
+ case 3:
+ cs.gainCash(1, item);
+ cs.gainCash(4, (item / 5000));
+ break;
+
+ default:
+ short qty = p.getRight().getRight().shortValue();
+ if (MapleItemInformationProvider.getInstance().isCash(item)) {
+ Item it = CashShop.generateCouponItem(item, qty);
+
+ cs.addToInventory(it);
+ couponPackage.add(it);
+ } else {
+ MapleInventoryManipulator.addById(c, item, qty, "", -1);
+ }
+
+ //c.announce(MaplePacketCreator.showCouponRedeemedItem(item));
+ break;
+ }
+ }
+
+ if (!couponPackage.isEmpty()) {
+ c.announce(MaplePacketCreator.showBoughtCashPackage(couponPackage, c.getAccID()));
+ } else {
+ c.announce(MaplePacketCreator.showBoughtQuestItem(0));
+ }
+
+ c.enableCSActions();
}
+ } finally {
+ c.releaseClient();
}
- switch (type) {
- case 0:
- case 1:
- case 2:
- c.getPlayer().getCashShop().gainCash(type, item);
- break;
- case 3:
- c.getPlayer().getCashShop().gainCash(0, item);
- c.getPlayer().getCashShop().gainCash(2, (item / 5000));
- break;
- case 4:
- MapleInventoryManipulator.addById(c, item, (short) 1, null, -1, -1);
- c.announce(MaplePacketCreator.showCouponRedeemedItem(item));
- break;
- case 5:
- c.getPlayer().getCashShop().gainCash(0, item);
- break;
- }
- c.announce(MaplePacketCreator.showCash(c.getPlayer()));
- } else {
- //c.announce(MaplePacketCreator.wrongCouponCode());
}
- c.announce(MaplePacketCreator.enableCSUse());
- }
-
- private int getNXCode(String code, String type) {
- int item = -1;
- try {
- Connection con = DatabaseConnection.getConnection();
- PreparedStatement ps = con.prepareStatement("SELECT `" + type + "` FROM nxcode WHERE code = ?");
- ps.setString(1, code);
- ResultSet rs = ps.executeQuery();
- while (rs.next()) {
- item = rs.getInt(type);
- }
- rs.close();
- ps.close();
- con.close();
- } catch (SQLException ex) {
- ex.printStackTrace();
- }
- return item;
- }
-
- private boolean getNXCodeValid(String code, boolean validcode) {
- try {
- Connection con = DatabaseConnection.getConnection();
- PreparedStatement ps = con.prepareStatement("SELECT `valid` FROM nxcode WHERE code = ?");
- ps.setString(1, code);
- ResultSet rs = ps.executeQuery();
- while (rs.next()) {
- validcode = rs.getInt("valid") != 0;
- }
- rs.close();
- ps.close();
- con.close();
- } catch (SQLException ex) {
- ex.printStackTrace();
- }
- return validcode;
}
}
diff --git a/src/net/server/channel/handlers/EnterMTSHandler.java b/src/net/server/channel/handlers/EnterMTSHandler.java
index 057b81052a..116530e224 100644
--- a/src/net/server/channel/handlers/EnterMTSHandler.java
+++ b/src/net/server/channel/handlers/EnterMTSHandler.java
@@ -111,7 +111,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
ex.printStackTrace();
}
chr.getCashShop().open(true);// xD
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(MaplePacketCreator.MTSWantedListingOver(0, 0));
c.announce(MaplePacketCreator.showMTSCash(c.getPlayer()));
List items = new ArrayList<>();
diff --git a/src/net/server/channel/handlers/FaceExpressionHandler.java b/src/net/server/channel/handlers/FaceExpressionHandler.java
index ed65bd715e..dd6d0cb577 100644
--- a/src/net/server/channel/handlers/FaceExpressionHandler.java
+++ b/src/net/server/channel/handlers/FaceExpressionHandler.java
@@ -40,13 +40,13 @@ public final class FaceExpressionHandler extends AbstractMaplePacketHandler {
}
}
- if(c.trylockClient()) {
+ if(c.tryacquireClient()) {
try { // expecting players never intends to wear the emote 0 (default face, that changes back after 5sec timeout)
if (emote != 0 && chr.isLoggedinWorld()) {
chr.changeFaceExpression(emote);
}
} finally {
- c.unlockClient();
+ c.releaseClient();
}
}
}
diff --git a/src/net/server/channel/handlers/ItemRewardHandler.java b/src/net/server/channel/handlers/ItemRewardHandler.java
index 1b9b8e6872..a4da7b00d3 100644
--- a/src/net/server/channel/handlers/ItemRewardHandler.java
+++ b/src/net/server/channel/handlers/ItemRewardHandler.java
@@ -60,7 +60,7 @@ public final class ItemRewardHandler extends AbstractMaplePacketHandler {
}
MapleInventoryManipulator.addFromDrop(c, item, false);
} else {
- MapleInventoryManipulator.addById(c, reward.itemid, reward.quantity);
+ MapleInventoryManipulator.addById(c, reward.itemid, reward.quantity, "", -1);
}
MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, itemId, 1, false, false);
if (reward.worldmsg != null) {
diff --git a/src/net/server/channel/handlers/MTSHandler.java b/src/net/server/channel/handlers/MTSHandler.java
index 39ae1ab8b4..054e6f7261 100644
--- a/src/net/server/channel/handlers/MTSHandler.java
+++ b/src/net/server/channel/handlers/MTSHandler.java
@@ -214,7 +214,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
c.getPlayer().gainMeso(-5000, false);
c.announce(MaplePacketCreator.MTSConfirmSell());
c.announce(getMTS(1, 0, 0));
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(MaplePacketCreator.transferInventory(getTransfer(c.getPlayer().getId())));
c.announce(MaplePacketCreator.notYetSoldInv(getNotYetSold(c.getPlayer().getId())));
}
@@ -240,7 +240,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
}
c.getPlayer().changeTab(tab);
c.getPlayer().changeType(type);
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(MaplePacketCreator.transferInventory(getTransfer(c.getPlayer().getId())));
c.announce(MaplePacketCreator.notYetSoldInv(getNotYetSold(c.getPlayer().getId())));
} else if (op == 6) { //search
@@ -253,7 +253,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
c.getPlayer().changeTab(tab);
c.getPlayer().changeType(type);
c.getPlayer().changeCI(ci);
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(MaplePacketCreator.enableActions());
c.announce(getMTSSearch(tab, type, ci, search, c.getPlayer().getCurrentPage()));
c.announce(MaplePacketCreator.showMTSCash(c.getPlayer()));
@@ -277,7 +277,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
} catch (SQLException e) {
e.printStackTrace();
}
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(getMTS(c.getPlayer().getCurrentTab(), c.getPlayer().getCurrentType(), c.getPlayer().getCurrentPage()));
c.announce(MaplePacketCreator.notYetSoldInv(getNotYetSold(c.getPlayer().getId())));
c.announce(MaplePacketCreator.transferInventory(getTransfer(c.getPlayer().getId())));
@@ -331,7 +331,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
pse.executeUpdate();
}
MapleInventoryManipulator.addFromDrop(c, i, false);
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(getCart(c.getPlayer().getId()));
c.announce(getMTS(c.getPlayer().getCurrentTab(), c.getPlayer().getCurrentType(), c.getPlayer().getCurrentPage()));
c.announce(MaplePacketCreator.MTSConfirmTransfer(i.getQuantity(), i.getPosition()));
@@ -374,7 +374,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
e.printStackTrace();
}
c.announce(getMTS(c.getPlayer().getCurrentTab(), c.getPlayer().getCurrentType(), c.getPlayer().getCurrentPage()));
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(MaplePacketCreator.enableActions());
c.announce(MaplePacketCreator.transferInventory(getTransfer(c.getPlayer().getId())));
c.announce(MaplePacketCreator.notYetSoldInv(getNotYetSold(c.getPlayer().getId())));
@@ -393,7 +393,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
e.printStackTrace();
}
c.announce(getCart(c.getPlayer().getId()));
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(MaplePacketCreator.transferInventory(getTransfer(c.getPlayer().getId())));
c.announce(MaplePacketCreator.notYetSoldInv(getNotYetSold(c.getPlayer().getId())));
} else if (op == 12) { //put item up for auction
@@ -445,7 +445,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
pse.executeUpdate();
pse.close();
c.getPlayer().getCashShop().gainCash(4, -price);
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(getMTS(c.getPlayer().getCurrentTab(), c.getPlayer().getCurrentType(), c.getPlayer().getCurrentPage()));
c.announce(MaplePacketCreator.MTSConfirmBuy());
c.announce(MaplePacketCreator.showMTSCash(c.getPlayer()));
@@ -507,7 +507,7 @@ public final class MTSHandler extends AbstractMaplePacketHandler {
pse.close();
c.getPlayer().getCashShop().gainCash(4, -price);
c.announce(getCart(c.getPlayer().getId()));
- c.announce(MaplePacketCreator.enableCSUse());
+ c.enableCSActions();
c.announce(MaplePacketCreator.MTSConfirmBuy());
c.announce(MaplePacketCreator.showMTSCash(c.getPlayer()));
c.announce(MaplePacketCreator.transferInventory(getTransfer(c.getPlayer().getId())));
diff --git a/src/net/server/channel/handlers/MagicDamageHandler.java b/src/net/server/channel/handlers/MagicDamageHandler.java
index f7df498812..b2288be8a6 100644
--- a/src/net/server/channel/handlers/MagicDamageHandler.java
+++ b/src/net/server/channel/handlers/MagicDamageHandler.java
@@ -39,7 +39,7 @@ public final class MagicDamageHandler extends AbstractDealDamageHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
- chr.setPetLootCd(currentServerTime());
+ //chr.setPetLootCd(currentServerTime());
/*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8);
if(timeElapsed < 300) {
diff --git a/src/net/server/channel/handlers/PetFoodHandler.java b/src/net/server/channel/handlers/PetFoodHandler.java
index 3996114d25..5a1e997e3c 100644
--- a/src/net/server/channel/handlers/PetFoodHandler.java
+++ b/src/net/server/channel/handlers/PetFoodHandler.java
@@ -23,6 +23,7 @@ package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
+import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import client.autoban.AutobanManager;
@@ -65,18 +66,25 @@ public final class PetFoodHandler extends AbstractMaplePacketHandler {
short pos = slea.readShort();
int itemId = slea.readInt();
- Item use = chr.getInventory(MapleInventoryType.USE).getItem(pos);
- if (use == null || (itemId / 10000) != 212 || use.getItemId() != itemId) {
- return;
- }
- c.lockClient();
- try {
- pet.gainClosenessFullness(chr, (pet.getFullness() <= 75) ? 1 : 0, 30, 1); // 25+ "emptyness" to get +1 closeness
- } finally {
- c.unlockClient();
+ if (c.tryacquireClient()) {
+ try {
+ MapleInventory useInv = chr.getInventory(MapleInventoryType.USE);
+ useInv.lockInventory();
+ try {
+ Item use = useInv.getItem(pos);
+ if (use == null || (itemId / 10000) != 212 || use.getItemId() != itemId || use.getQuantity() < 1) {
+ return;
+ }
+
+ pet.gainClosenessFullness(chr, (pet.getFullness() <= 75) ? 1 : 0, 30, 1); // 25+ "emptyness" to get +1 closeness
+ MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, pos, (short) 1, false);
+ } finally {
+ useInv.unlockInventory();
+ }
+ } finally {
+ c.releaseClient();
+ }
}
-
- MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, pos, (short) 1, false);
}
}
diff --git a/src/net/server/channel/handlers/PlayerInteractionHandler.java b/src/net/server/channel/handlers/PlayerInteractionHandler.java
index 375ce9ad8a..ae32614516 100644
--- a/src/net/server/channel/handlers/PlayerInteractionHandler.java
+++ b/src/net/server/channel/handlers/PlayerInteractionHandler.java
@@ -49,6 +49,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
/**
*
* @author Matze
+ * @author Ronan (concurrency safety & reviewed minigames)
*/
public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
public enum Action {
@@ -109,7 +110,7 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
}
}
- private static int estabilishMiniroomStatus(MapleCharacter chr, boolean isMinigame) {
+ private static int establishMiniroomStatus(MapleCharacter chr, boolean isMinigame) {
if (isMinigame && FieldLimit.CANNOTMINIGAME.check(chr.getMap().getFieldLimit())) {
return 11;
}
@@ -127,510 +128,564 @@ public final class PlayerInteractionHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
- byte mode = slea.readByte();
- final MapleCharacter chr = c.getPlayer();
+ if (!c.tryacquireClient()) { // thanks GabrielSin for pointing dupes within player interactions
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ }
- if (mode == Action.CREATE.getCode()) {
- if(!chr.isAlive()) { // thanks GabrielSin for pointing this
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(4));
- return;
- }
-
- byte createType = slea.readByte();
- if (createType == 3) {// trade
- MapleTrade.startTrade(chr);
- } else if (createType == 1) { // omok mini game
- int status = estabilishMiniroomStatus(chr, true);
- if (status > 0) {
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(status));
+ try {
+ byte mode = slea.readByte();
+ final MapleCharacter chr = c.getPlayer();
+
+ if (mode == Action.CREATE.getCode()) {
+ if(!chr.isAlive()) { // thanks GabrielSin for pointing this
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(4));
return;
}
-
- String desc = slea.readMapleAsciiString();
- int type = slea.readByte();
- String pw = slea.available() > 1 ? slea.readMapleAsciiString() : "";
-
- MapleMiniGame game = new MapleMiniGame(chr, desc, pw);
- chr.setMiniGame(game);
- game.setPieceType(type);
- game.setGameType(MiniGameType.OMOK);
- chr.getMap().addMapObject(game);
- chr.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(chr, 1, 0));
- game.sendOmok(c, type);
- } else if (createType == 2) { // matchcard
- int status = estabilishMiniroomStatus(chr, true);
- if (status > 0) {
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(status));
+
+ byte createType = slea.readByte();
+ if (createType == 3) {// trade
+ MapleTrade.startTrade(chr);
+ } else if (createType == 1) { // omok mini game
+ int status = establishMiniroomStatus(chr, true);
+ if (status > 0) {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(status));
+ return;
+ }
+
+ String desc = slea.readMapleAsciiString();
+ String pw;
+
+ if (slea.readByte() != 0) {
+ pw = slea.readMapleAsciiString();
+ } else {
+ pw = "";
+ }
+
+ int type = slea.readByte();
+ if (type > 11) {
+ type = 11;
+ } else if (type < 0) {
+ type = 0;
+ }
+ if (!chr.haveItem(4080000 + type)) {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(6));
+ return;
+ }
+
+ MapleMiniGame game = new MapleMiniGame(chr, desc, pw);
+ chr.setMiniGame(game);
+ game.setPieceType(type);
+ game.setGameType(MiniGameType.OMOK);
+ chr.getMap().addMapObject(game);
+ chr.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(chr, 1, 0));
+ game.sendOmok(c, type);
+ } else if (createType == 2) { // matchcard
+ int status = establishMiniroomStatus(chr, true);
+ if (status > 0) {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(status));
+ return;
+ }
+
+ String desc = slea.readMapleAsciiString();
+ String pw;
+
+ if (slea.readByte() != 0) {
+ pw = slea.readMapleAsciiString();
+ } else {
+ pw = "";
+ }
+
+ int type = slea.readByte();
+ if (type > 2) {
+ type = 2;
+ } else if (type < 0) {
+ type = 0;
+ }
+ if (!chr.haveItem(4080100)) {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(6));
+ return;
+ }
+
+ MapleMiniGame game = new MapleMiniGame(chr, desc, pw);
+ game.setPieceType(type);
+ if (type == 0) {
+ game.setMatchesToWin(6);
+ } else if (type == 1) {
+ game.setMatchesToWin(10);
+ } else if (type == 2) {
+ game.setMatchesToWin(15);
+ }
+ game.setGameType(MiniGameType.MATCH_CARD);
+ chr.setMiniGame(game);
+ chr.getMap().addMapObject(game);
+ chr.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(chr, 1, 0));
+ game.sendMatchCard(c, type);
+ } else if (createType == 4 || createType == 5) { // shop
+ if(!GameConstants.isFreeMarketRoom(chr.getMapId())) {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(15));
+ return;
+ }
+
+ int status = establishMiniroomStatus(chr, false);
+ if (status > 0) {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(status));
+ return;
+ }
+
+ String desc = slea.readMapleAsciiString();
+ slea.skip(3);
+ int itemId = slea.readInt();
+ if (chr.getInventory(MapleInventoryType.CASH).countById(itemId) < 1) {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(6));
+ return;
+ }
+
+ if (ItemConstants.isPlayerShop(itemId)) {
+ MaplePlayerShop shop = new MaplePlayerShop(chr, desc, itemId);
+ chr.setPlayerShop(shop);
+ chr.getMap().addMapObject(shop);
+ shop.sendShop(c);
+ c.getWorldServer().registerPlayerShop(shop);
+ //c.announce(MaplePacketCreator.getPlayerShopRemoveVisitor(1));
+ } else if (ItemConstants.isHiredMerchant(itemId)) {
+ MapleHiredMerchant merchant = new MapleHiredMerchant(chr, desc, itemId);
+ chr.setHiredMerchant(merchant);
+ c.getWorldServer().registerHiredMerchant(merchant);
+ chr.getClient().getChannelServer().addHiredMerchant(chr.getId(), merchant);
+ chr.announce(MaplePacketCreator.getHiredMerchant(chr, merchant, true));
+ }
+ }
+ } else if (mode == Action.INVITE.getCode()) {
+ int otherPlayer = slea.readInt();
+ if (chr.getId() == chr.getMap().getCharacterById(otherPlayer).getId()) {
return;
}
-
- String desc = slea.readMapleAsciiString();
- int type = slea.readByte();
- String pw = slea.available() > 1 ? slea.readMapleAsciiString() : "";
-
- MapleMiniGame game = new MapleMiniGame(chr, desc, pw);
- game.setPieceType(type);
- if (type == 0) {
- game.setMatchesToWin(6);
- } else if (type == 1) {
- game.setMatchesToWin(10);
- } else if (type == 2) {
- game.setMatchesToWin(15);
- }
- game.setGameType(MiniGameType.MATCH_CARD);
- chr.setMiniGame(game);
- chr.getMap().addMapObject(game);
- chr.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(chr, 1, 0));
- game.sendMatchCard(c, type);
- } else if (createType == 4 || createType == 5) { // shop
- if(!GameConstants.isFreeMarketRoom(chr.getMapId())) {
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(15));
- return;
- }
-
- int status = estabilishMiniroomStatus(chr, false);
- if (status > 0) {
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(status));
- return;
- }
-
- String desc = slea.readMapleAsciiString();
- slea.skip(3);
- int itemId = slea.readInt();
- if (chr.getInventory(MapleInventoryType.CASH).countById(itemId) < 1) {
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(6));
- return;
- }
-
- if (ItemConstants.isPlayerShop(itemId)) {
- MaplePlayerShop shop = new MaplePlayerShop(chr, desc, itemId);
- chr.setPlayerShop(shop);
- chr.getMap().addMapObject(shop);
- shop.sendShop(c);
- c.getWorldServer().registerPlayerShop(shop);
- //c.announce(MaplePacketCreator.getPlayerShopRemoveVisitor(1));
- } else if (ItemConstants.isHiredMerchant(itemId)) {
- MapleHiredMerchant merchant = new MapleHiredMerchant(chr, desc, itemId);
- chr.setHiredMerchant(merchant);
- c.getWorldServer().registerHiredMerchant(merchant);
- chr.getClient().getChannelServer().addHiredMerchant(chr.getId(), merchant);
- chr.announce(MaplePacketCreator.getHiredMerchant(chr, merchant, true));
- }
- }
- } else if (mode == Action.INVITE.getCode()) {
- int otherPlayer = slea.readInt();
- if (chr.getId() == chr.getMap().getCharacterById(otherPlayer).getId()) {
- return;
- }
- MapleTrade.inviteTrade(chr, chr.getMap().getCharacterById(otherPlayer));
- } else if (mode == Action.DECLINE.getCode()) {
- MapleTrade.declineTrade(chr);
- } else if (mode == Action.VISIT.getCode()) {
- if (chr.getTrade() != null && chr.getTrade().getPartner() != null) {
- if (!chr.getTrade().isFullTrade() && !chr.getTrade().getPartner().isFullTrade()) {
- MapleTrade.visitTrade(chr, chr.getTrade().getPartner().getChr());
- } else {
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(2));
- return;
- }
- } else {
- if (isTradeOpen(chr)) return;
-
- int oid = slea.readInt();
- MapleMapObject ob = chr.getMap().getMapObject(oid);
- if (ob instanceof MaplePlayerShop) {
- MaplePlayerShop shop = (MaplePlayerShop) ob;
- shop.visitShop(chr);
- } else if (ob instanceof MapleMiniGame) {
- slea.skip(1);
- String pw = slea.available() > 1 ? slea.readMapleAsciiString() : "";
-
- MapleMiniGame game = (MapleMiniGame) ob;
- if(game.checkPassword(pw)) {
- if (game.hasFreeSlot() && !game.isVisitor(chr)) {
- game.addVisitor(chr);
- chr.setMiniGame(game);
- switch (game.getGameType()) {
- case OMOK:
- game.sendOmok(c, game.getPieceType());
- break;
- case MATCH_CARD:
- game.sendMatchCard(c, game.getPieceType());
- break;
+ MapleTrade.inviteTrade(chr, chr.getMap().getCharacterById(otherPlayer));
+ } else if (mode == Action.DECLINE.getCode()) {
+ MapleTrade.declineTrade(chr);
+ } else if (mode == Action.VISIT.getCode()) {
+ if (chr.getTrade() != null && chr.getTrade().getPartner() != null) {
+ if (!chr.getTrade().isFullTrade() && !chr.getTrade().getPartner().isFullTrade()) {
+ MapleTrade.visitTrade(chr, chr.getTrade().getPartner().getChr());
+ } else {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(2));
+ return;
+ }
+ } else {
+ if (isTradeOpen(chr)) return;
+
+ int oid = slea.readInt();
+ MapleMapObject ob = chr.getMap().getMapObject(oid);
+ if (ob instanceof MaplePlayerShop) {
+ MaplePlayerShop shop = (MaplePlayerShop) ob;
+ shop.visitShop(chr);
+ } else if (ob instanceof MapleMiniGame) {
+ slea.skip(1);
+ String pw = slea.available() > 1 ? slea.readMapleAsciiString() : "";
+
+ MapleMiniGame game = (MapleMiniGame) ob;
+ if(game.checkPassword(pw)) {
+ if (game.hasFreeSlot() && !game.isVisitor(chr)) {
+ game.addVisitor(chr);
+ chr.setMiniGame(game);
+ switch (game.getGameType()) {
+ case OMOK:
+ game.sendOmok(c, game.getPieceType());
+ break;
+ case MATCH_CARD:
+ game.sendMatchCard(c, game.getPieceType());
+ break;
+ }
+ } else {
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(2));
}
} else {
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(2));
+ chr.getClient().announce(MaplePacketCreator.getMiniRoomError(22));
}
- } else {
- chr.getClient().announce(MaplePacketCreator.getMiniRoomError(22));
+ } else if (ob instanceof MapleHiredMerchant && chr.getHiredMerchant() == null) {
+ MapleHiredMerchant merchant = (MapleHiredMerchant) ob;
+ merchant.visitShop(chr);
}
- } else if (ob instanceof MapleHiredMerchant && chr.getHiredMerchant() == null) {
- MapleHiredMerchant merchant = (MapleHiredMerchant) ob;
- merchant.visitShop(chr);
}
- }
- } else if (mode == Action.CHAT.getCode()) { // chat lol
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if (chr.getTrade() != null) {
- chr.getTrade().chat(slea.readMapleAsciiString());
- } else if (chr.getPlayerShop() != null) { //mini game
- MaplePlayerShop shop = chr.getPlayerShop();
- if (shop != null) {
- shop.chat(c, slea.readMapleAsciiString());
- }
- } else if (chr.getMiniGame() != null) {
- MapleMiniGame game = chr.getMiniGame();
- if (game != null) {
- game.chat(c, slea.readMapleAsciiString());
- }
- } else if (merchant != null) {
- merchant.sendMessage(chr, slea.readMapleAsciiString());
- }
- } else if (mode == Action.EXIT.getCode()) {
- if (chr.getTrade() != null) {
- MapleTrade.cancelTrade(chr);
- } else {
- chr.closePlayerShop();
- chr.closeMiniGame();
- chr.closeHiredMerchant(true);
- }
- } else if (mode == Action.OPEN.getCode()) {
- if (isTradeOpen(chr)) return;
-
- MaplePlayerShop shop = chr.getPlayerShop();
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if (shop != null && shop.isOwner(chr)) {
- slea.readByte();//01
-
- if(ServerConstants.USE_ERASE_PERMIT_ON_OPENSHOP) {
- try {
- MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, shop.getItemId(), 1, true, false);
- } catch(RuntimeException re) {} // fella does not have a player shop permit...
- }
-
- chr.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(shop));
- shop.setOpen(true);
- } else if (merchant != null && merchant.isOwner(chr)) {
- chr.setHasMerchant(true);
- merchant.setOpen(true);
- chr.getMap().addMapObject(merchant);
- chr.setHiredMerchant(null);
- chr.getMap().broadcastMessage(MaplePacketCreator.spawnHiredMerchantBox(merchant));
- slea.readByte();
- }
- } else if (mode == Action.READY.getCode()) {
- MapleMiniGame game = chr.getMiniGame();
- game.broadcast(MaplePacketCreator.getMiniGameReady(game));
- } else if (mode == Action.UN_READY.getCode()) {
- MapleMiniGame game = chr.getMiniGame();
- game.broadcast(MaplePacketCreator.getMiniGameUnReady(game));
- } else if (mode == Action.START.getCode()) {
- MapleMiniGame game = chr.getMiniGame();
- if (game.getGameType().equals("omok")) {
- game.broadcast(MaplePacketCreator.getMiniGameStart(game, game.getLoser()));
- chr.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(game.getOwner(), 2, 1));
- }
- if (game.getGameType().equals("matchcard")) {
- game.shuffleList();
- game.broadcast(MaplePacketCreator.getMatchCardStart(game, game.getLoser()));
- chr.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(game.getOwner(), 2, 1));
- }
- } else if (mode == Action.GIVE_UP.getCode()) {
- MapleMiniGame game = chr.getMiniGame();
- if (game.getGameType().equals("omok")) {
- if (game.isOwner(chr)) {
- game.broadcast(MaplePacketCreator.getMiniGameOwnerForfeit(game));
- } else {
- game.broadcast(MaplePacketCreator.getMiniGameVisitorForfeit(game));
- }
- }
- if (game.getGameType().equals("matchcard")) {
- if (game.isOwner(chr)) {
- game.broadcast(MaplePacketCreator.getMatchCardVisitorWin(game));
- } else {
- game.broadcast(MaplePacketCreator.getMatchCardOwnerWin(game));
- }
- }
- } else if (mode == Action.REQUEST_TIE.getCode()) {
- MapleMiniGame game = chr.getMiniGame();
- if (game.isOwner(chr)) {
- game.broadcastToVisitor(MaplePacketCreator.getMiniGameRequestTie(game));
- } else {
- game.getOwner().getClient().announce(MaplePacketCreator.getMiniGameRequestTie(game));
- }
- } else if (mode == Action.ANSWER_TIE.getCode()) {
- MapleMiniGame game = chr.getMiniGame();
- slea.readByte();
- if (game.getGameType().equals("omok")) {
- game.broadcast(MaplePacketCreator.getMiniGameTie(game));
- }
- if (game.getGameType().equals("matchcard")) {
- game.broadcast(MaplePacketCreator.getMatchCardTie(game));
- }
- } else if (mode == Action.SKIP.getCode()) {
- MapleMiniGame game = chr.getMiniGame();
- if (game.isOwner(chr)) {
- game.broadcast(MaplePacketCreator.getMiniGameSkipOwner(game));
- } else {
- game.broadcast(MaplePacketCreator.getMiniGameSkipVisitor(game));
- }
- } else if (mode == Action.MOVE_OMOK.getCode()) {
- int x = slea.readInt(); // x point
- int y = slea.readInt(); // y point
- int type = slea.readByte(); // piece ( 1 or 2; Owner has one piece, visitor has another, it switches every game.)
- chr.getMiniGame().setPiece(x, y, type, chr);
- } else if (mode == Action.SELECT_CARD.getCode()) {
- int turn = slea.readByte(); // 1st turn = 1; 2nd turn = 0
- int slot = slea.readByte(); // slot
- MapleMiniGame game = chr.getMiniGame();
- int firstslot = game.getFirstSlot();
- if (turn == 1) {
- game.setFirstSlot(slot);
- if (game.isOwner(chr)) {
- game.broadcastToVisitor(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, turn));
- } else {
- game.getOwner().getClient().announce(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, turn));
- }
- } else if ((game.getCardId(firstslot + 1)) == (game.getCardId(slot + 1))) {
- if (game.isOwner(chr)) {
- game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 2));
- game.setOwnerPoints();
- } else {
- game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 3));
- game.setVisitorPoints();
- }
- } else if (game.isOwner(chr)) {
- game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 0));
- } else {
- game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 1));
- }
- } else if (mode == Action.SET_MESO.getCode()) {
- chr.getTrade().setMeso(slea.readInt());
- } else if (mode == Action.SET_ITEMS.getCode()) {
- MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
- MapleInventoryType ivType = MapleInventoryType.getByType(slea.readByte());
- Item item = chr.getInventory(ivType).getItem(slea.readShort());
- short quantity = slea.readShort();
- byte targetSlot = slea.readByte();
- if (quantity < 1 || quantity > item.getQuantity()) {
- c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough quantity of the item."));
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
- if (chr.getTrade() != null) {
- if ((quantity <= item.getQuantity() && quantity >= 0) || ItemConstants.isRechargeable(item.getItemId())) {
- if (ii.isDropRestricted(item.getItemId())) { // ensure that undroppable items do not make it to the trade window
- if (!MapleKarmaManipulator.hasKarmaFlag(item)) {
- c.announce(MaplePacketCreator.serverNotice(1, "That item is untradeable."));
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
+ } else if (mode == Action.CHAT.getCode()) { // chat lol
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if (chr.getTrade() != null) {
+ chr.getTrade().chat(slea.readMapleAsciiString());
+ } else if (chr.getPlayerShop() != null) { //mini game
+ MaplePlayerShop shop = chr.getPlayerShop();
+ if (shop != null) {
+ shop.chat(c, slea.readMapleAsciiString());
}
- Item tradeItem = item.copy();
- if (ItemConstants.isRechargeable(item.getItemId())) {
- tradeItem.setQuantity(item.getQuantity());
- MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), item.getQuantity(), true);
- } else {
- tradeItem.setQuantity(quantity);
- MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true);
+ } else if (chr.getMiniGame() != null) {
+ MapleMiniGame game = chr.getMiniGame();
+ if (game != null) {
+ game.chat(c, slea.readMapleAsciiString());
}
- tradeItem.setPosition(targetSlot);
- chr.getTrade().addItem(tradeItem);
+ } else if (merchant != null) {
+ merchant.sendMessage(chr, slea.readMapleAsciiString());
}
- }
- } else if (mode == Action.CONFIRM.getCode()) {
- MapleTrade.completeTrade(chr);
- } else if (mode == Action.ADD_ITEM.getCode() || mode == Action.PUT_ITEM.getCode()) {
- if (isTradeOpen(chr)) return;
-
- MapleInventoryType ivType = MapleInventoryType.getByType(slea.readByte());
- short slot = slea.readShort();
- short bundles = slea.readShort();
- Item ivItem = chr.getInventory(ivType).getItem(slot);
-
- if (ivItem == null || ivItem.getFlag() == ItemConstants.UNTRADEABLE) {
- c.announce(MaplePacketCreator.serverNotice(1, "Could not perform shop operation with that item."));
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
-
- short perBundle = slea.readShort();
-
- if (ItemConstants.isRechargeable(ivItem.getItemId())) {
- perBundle = 1;
- bundles = 1;
- } else if (chr.getItemQuantity(ivItem.getItemId(), false) < perBundle * bundles) {
- c.announce(MaplePacketCreator.serverNotice(1, "Could not perform shop operation with that item."));
- c.announce(MaplePacketCreator.enableActions());
- return;
- }
-
- int price = slea.readInt();
- if (perBundle <= 0 || perBundle * bundles > 2000 || bundles <= 0 || price <= 0 || price > Integer.MAX_VALUE) {
- AutobanFactory.PACKET_EDIT.alert(chr, chr.getName() + " tried to packet edit with hired merchants.");
- FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " might of possibly packet edited Hired Merchants\nperBundle: " + perBundle + "\nperBundle * bundles (This multiplied cannot be greater than 2000): " + perBundle * bundles + "\nbundles: " + bundles + "\nprice: " + price);
- return;
- }
-
- if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && MapleItemInformationProvider.getInstance().isCash(ivItem.getItemId())) {
- c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be sold on the Player Store."));
- return;
- }
-
- if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(ivItem.getItemId())) {
- c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be sold on the Player Store."));
- return;
- }
-
- Item sellItem = ivItem.copy();
- if(!ItemConstants.isRechargeable(ivItem.getItemId())) {
- sellItem.setQuantity(perBundle);
- }
-
- MaplePlayerShopItem shopItem = new MaplePlayerShopItem(sellItem, bundles, price);
- MaplePlayerShop shop = chr.getPlayerShop();
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if (shop != null && shop.isOwner(chr)) {
- if (shop.isOpen()) {
- c.announce(MaplePacketCreator.serverNotice(1, "You can't sell it anymore."));
- return;
+ } else if (mode == Action.EXIT.getCode()) {
+ if (chr.getTrade() != null) {
+ MapleTrade.cancelTrade(chr);
+ } else {
+ chr.closePlayerShop();
+ chr.closeMiniGame();
+ chr.closeHiredMerchant(true);
}
-
- shop.addItem(shopItem);
- c.announce(MaplePacketCreator.getPlayerShopItemUpdate(shop));
- } else if (merchant != null && merchant.isOwner(chr)) {
- if (merchant.isOpen()) {
- c.announce(MaplePacketCreator.serverNotice(1, "You can't sell it anymore."));
- return;
- }
-
- merchant.addItem(shopItem);
- c.announce(MaplePacketCreator.updateHiredMerchant(merchant, chr));
- }
- if (ItemConstants.isRechargeable(ivItem.getItemId())) {
- MapleInventoryManipulator.removeFromSlot(c, ivType, slot, ivItem.getQuantity(), true);
- } else {
- MapleInventoryManipulator.removeFromSlot(c, ivType, slot, (short) (bundles * perBundle), true);
- }
- } else if (mode == Action.REMOVE_ITEM.getCode()) {
- if (isTradeOpen(chr)) return;
-
- MaplePlayerShop shop = chr.getPlayerShop();
- if (shop != null && shop.isOwner(chr)) {
- if (shop.isOpen()) {
- c.announce(MaplePacketCreator.serverNotice(1, "You can't take it with the store open."));
- return;
- }
-
- int slot = slea.readShort();
- if (slot >= shop.getItems().size() || slot < 0) {
- AutobanFactory.PACKET_EDIT.alert(chr, chr.getName() + " tried to packet edit with a player shop.");
- FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " tried to remove item at slot " + slot + "\r\n");
- c.disconnect(true, false);
- return;
- }
-
- shop.takeItemBack(slot, chr);
- }
- } else if (mode == Action.MERCHANT_MESO.getCode()) {
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if (merchant == null) return;
-
- merchant.withdrawMesos(chr);
- } else if (mode == Action.MERCHANT_ORGANIZE.getCode()) {
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if (merchant == null || !merchant.isOwner(chr)) return;
-
- merchant.withdrawMesos(chr);
- merchant.clearInexistentItems();
-
- if (merchant.getItems().isEmpty()) {
- merchant.closeOwnerMerchant(chr);
- return;
- }
- c.announce(MaplePacketCreator.updateHiredMerchant(merchant, chr));
+ } else if (mode == Action.OPEN.getCode()) {
+ if (isTradeOpen(chr)) return;
- } else if (mode == Action.BUY.getCode() || mode == Action.MERCHANT_BUY.getCode()) {
- if (isTradeOpen(chr)) return;
-
- int itemid = slea.readByte();
- short quantity = slea.readShort();
- if (quantity < 1) {
- AutobanFactory.PACKET_EDIT.alert(chr, chr.getName() + " tried to packet edit with a hired merchant and or player shop.");
- FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " tried to buy item " + itemid + " with quantity " + quantity + "\r\n");
- c.disconnect(true, false);
- return;
- }
- MaplePlayerShop shop = chr.getPlayerShop();
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if (shop != null && shop.isVisitor(chr)) {
- shop.buy(c, itemid, quantity);
- shop.broadcast(MaplePacketCreator.getPlayerShopItemUpdate(shop));
- } else if (merchant != null && !merchant.isOwner(chr)) {
- merchant.buy(c, itemid, quantity);
- merchant.broadcastToVisitorsThreadsafe(MaplePacketCreator.updateHiredMerchant(merchant, chr));
- }
- } else if (mode == Action.TAKE_ITEM_BACK.getCode()) {
- if (isTradeOpen(chr)) return;
-
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if (merchant != null && merchant.isOwner(chr)) {
- if (merchant.isOpen()) {
- c.announce(MaplePacketCreator.serverNotice(1, "You can't take it with the store open."));
+ MaplePlayerShop shop = chr.getPlayerShop();
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if (shop != null && shop.isOwner(chr)) {
+ slea.readByte();//01
+
+ if(ServerConstants.USE_ERASE_PERMIT_ON_OPENSHOP) {
+ try {
+ MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, shop.getItemId(), 1, true, false);
+ } catch(RuntimeException re) {} // fella does not have a player shop permit...
+ }
+
+ chr.getMap().broadcastMessage(MaplePacketCreator.updatePlayerShopBox(shop));
+ shop.setOpen(true);
+ } else if (merchant != null && merchant.isOwner(chr)) {
+ chr.setHasMerchant(true);
+ merchant.setOpen(true);
+ chr.getMap().addMapObject(merchant);
+ chr.setHiredMerchant(null);
+ chr.getMap().broadcastMessage(MaplePacketCreator.spawnHiredMerchantBox(merchant));
+ slea.readByte();
+ }
+ } else if (mode == Action.READY.getCode()) {
+ MapleMiniGame game = chr.getMiniGame();
+ game.broadcast(MaplePacketCreator.getMiniGameReady(game));
+ } else if (mode == Action.UN_READY.getCode()) {
+ MapleMiniGame game = chr.getMiniGame();
+ game.broadcast(MaplePacketCreator.getMiniGameUnReady(game));
+ } else if (mode == Action.START.getCode()) {
+ MapleMiniGame game = chr.getMiniGame();
+ if (game.getGameType().equals(MiniGameType.OMOK)) {
+ game.minigameMatchStarted();
+ game.broadcast(MaplePacketCreator.getMiniGameStart(game, game.getLoser()));
+ chr.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(game.getOwner(), 2, 1));
+ } else if (game.getGameType().equals(MiniGameType.MATCH_CARD)) {
+ game.minigameMatchStarted();
+ game.shuffleList();
+ game.broadcast(MaplePacketCreator.getMatchCardStart(game, game.getLoser()));
+ chr.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(game.getOwner(), 2, 1));
+ }
+ } else if (mode == Action.GIVE_UP.getCode()) {
+ MapleMiniGame game = chr.getMiniGame();
+ if (game.getGameType().equals(MiniGameType.OMOK)) {
+ if (game.isOwner(chr)) {
+ game.minigameMatchVisitorWins(true);
+ } else {
+ game.minigameMatchOwnerWins(true);
+ }
+ } else if (game.getGameType().equals(MiniGameType.MATCH_CARD)) {
+ if (game.isOwner(chr)) {
+ game.minigameMatchVisitorWins(true);
+ } else {
+ game.minigameMatchOwnerWins(true);
+ }
+ }
+ } else if (mode == Action.REQUEST_TIE.getCode()) {
+ MapleMiniGame game = chr.getMiniGame();
+ if (!game.isTieDenied(chr)) {
+ if (game.isOwner(chr)) {
+ game.broadcastToVisitor(MaplePacketCreator.getMiniGameRequestTie(game));
+ } else {
+ game.broadcastToOwner(MaplePacketCreator.getMiniGameRequestTie(game));
+ }
+ }
+ } else if (mode == Action.ANSWER_TIE.getCode()) {
+ MapleMiniGame game = chr.getMiniGame();
+ if (slea.readByte() != 0) {
+ game.minigameMatchDraw();
+ } else {
+ game.denyTie(chr);
+
+ if (game.isOwner(chr)) {
+ game.broadcastToVisitor(MaplePacketCreator.getMiniGameDenyTie(game));
+ } else {
+ game.broadcastToOwner(MaplePacketCreator.getMiniGameDenyTie(game));
+ }
+ }
+ } else if (mode == Action.SKIP.getCode()) {
+ MapleMiniGame game = chr.getMiniGame();
+ if (game.isOwner(chr)) {
+ game.broadcast(MaplePacketCreator.getMiniGameSkipOwner(game));
+ } else {
+ game.broadcast(MaplePacketCreator.getMiniGameSkipVisitor(game));
+ }
+ } else if (mode == Action.MOVE_OMOK.getCode()) {
+ int x = slea.readInt(); // x point
+ int y = slea.readInt(); // y point
+ int type = slea.readByte(); // piece ( 1 or 2; Owner has one piece, visitor has another, it switches every game.)
+ chr.getMiniGame().setPiece(x, y, type, chr);
+ } else if (mode == Action.SELECT_CARD.getCode()) {
+ int turn = slea.readByte(); // 1st turn = 1; 2nd turn = 0
+ int slot = slea.readByte(); // slot
+ MapleMiniGame game = chr.getMiniGame();
+ int firstslot = game.getFirstSlot();
+ if (turn == 1) {
+ game.setFirstSlot(slot);
+ if (game.isOwner(chr)) {
+ game.broadcastToVisitor(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, turn));
+ } else {
+ game.getOwner().getClient().announce(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, turn));
+ }
+ } else if ((game.getCardId(firstslot)) == (game.getCardId(slot))) {
+ if (game.isOwner(chr)) {
+ game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 2));
+ game.setOwnerPoints();
+ } else {
+ game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 3));
+ game.setVisitorPoints();
+ }
+ } else if (game.isOwner(chr)) {
+ game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 0));
+ } else {
+ game.broadcast(MaplePacketCreator.getMatchCardSelect(game, turn, slot, firstslot, 1));
+ }
+ } else if (mode == Action.SET_MESO.getCode()) {
+ chr.getTrade().setMeso(slea.readInt());
+ } else if (mode == Action.SET_ITEMS.getCode()) {
+ MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
+ MapleInventoryType ivType = MapleInventoryType.getByType(slea.readByte());
+ Item item = chr.getInventory(ivType).getItem(slea.readShort());
+ short quantity = slea.readShort();
+ byte targetSlot = slea.readByte();
+
+ if (item == null) {
+ c.announce(MaplePacketCreator.serverNotice(1, "Invalid item description."));
+ c.announce(MaplePacketCreator.enableActions());
return;
}
-
- int slot = slea.readShort();
- if (slot >= merchant.getItems().size() || slot < 0) {
- AutobanFactory.PACKET_EDIT.alert(chr, chr.getName() + " tried to packet edit with a hired merchant.");
- FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " tried to remove item at slot " + slot + "\r\n");
+
+ if (quantity < 1 || quantity > item.getQuantity()) {
+ c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough quantity of the item."));
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ }
+ if (chr.getTrade() != null) {
+ if ((quantity <= item.getQuantity() && quantity >= 0) || ItemConstants.isRechargeable(item.getItemId())) {
+ if (ii.isDropRestricted(item.getItemId())) { // ensure that undroppable items do not make it to the trade window
+ if (!MapleKarmaManipulator.hasKarmaFlag(item)) {
+ c.announce(MaplePacketCreator.serverNotice(1, "That item is untradeable."));
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ }
+ }
+ Item tradeItem = item.copy();
+ if (ItemConstants.isRechargeable(item.getItemId())) {
+ tradeItem.setQuantity(item.getQuantity());
+ MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), item.getQuantity(), true);
+ } else {
+ tradeItem.setQuantity(quantity);
+ MapleInventoryManipulator.removeFromSlot(c, ivType, item.getPosition(), quantity, true);
+ }
+ tradeItem.setPosition(targetSlot);
+ chr.getTrade().addItem(tradeItem);
+ }
+ }
+ } else if (mode == Action.CONFIRM.getCode()) {
+ MapleTrade.completeTrade(chr);
+ } else if (mode == Action.ADD_ITEM.getCode() || mode == Action.PUT_ITEM.getCode()) {
+ if (isTradeOpen(chr)) return;
+
+ MapleInventoryType ivType = MapleInventoryType.getByType(slea.readByte());
+ short slot = slea.readShort();
+ short bundles = slea.readShort();
+ Item ivItem = chr.getInventory(ivType).getItem(slot);
+
+ if (ivItem == null || ivItem.isUntradeable()) {
+ c.announce(MaplePacketCreator.serverNotice(1, "Could not perform shop operation with that item."));
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ }
+
+ short perBundle = slea.readShort();
+
+ if (ItemConstants.isRechargeable(ivItem.getItemId())) {
+ perBundle = 1;
+ bundles = 1;
+ } else if (chr.getItemQuantity(ivItem.getItemId(), false) < perBundle * bundles) {
+ c.announce(MaplePacketCreator.serverNotice(1, "Could not perform shop operation with that item."));
+ c.announce(MaplePacketCreator.enableActions());
+ return;
+ }
+
+ int price = slea.readInt();
+ if (perBundle <= 0 || perBundle * bundles > 2000 || bundles <= 0 || price <= 0 || price > Integer.MAX_VALUE) {
+ AutobanFactory.PACKET_EDIT.alert(chr, chr.getName() + " tried to packet edit with hired merchants.");
+ FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " might of possibly packet edited Hired Merchants\nperBundle: " + perBundle + "\nperBundle * bundles (This multiplied cannot be greater than 2000): " + perBundle * bundles + "\nbundles: " + bundles + "\nprice: " + price);
+ return;
+ }
+
+ if(ServerConstants.USE_ENFORCE_UNMERCHABLE_CASH && MapleItemInformationProvider.getInstance().isCash(ivItem.getItemId())) {
+ c.announce(MaplePacketCreator.serverNotice(1, "Cash items are not allowed to be sold on the Player Store."));
+ return;
+ }
+
+ if (ServerConstants.USE_ENFORCE_UNMERCHABLE_PET && ItemConstants.isPet(ivItem.getItemId())) {
+ c.announce(MaplePacketCreator.serverNotice(1, "Pets are not allowed to be sold on the Player Store."));
+ return;
+ }
+
+ Item sellItem = ivItem.copy();
+ if(!ItemConstants.isRechargeable(ivItem.getItemId())) {
+ sellItem.setQuantity(perBundle);
+ }
+
+ MaplePlayerShopItem shopItem = new MaplePlayerShopItem(sellItem, bundles, price);
+ MaplePlayerShop shop = chr.getPlayerShop();
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if (shop != null && shop.isOwner(chr)) {
+ if (shop.isOpen()) {
+ c.announce(MaplePacketCreator.serverNotice(1, "You can't sell it anymore."));
+ return;
+ }
+
+ shop.addItem(shopItem);
+ c.announce(MaplePacketCreator.getPlayerShopItemUpdate(shop));
+ } else if (merchant != null && merchant.isOwner(chr)) {
+ if (merchant.isOpen()) {
+ c.announce(MaplePacketCreator.serverNotice(1, "You can't sell it anymore."));
+ return;
+ }
+
+ merchant.addItem(shopItem);
+ c.announce(MaplePacketCreator.updateHiredMerchant(merchant, chr));
+ }
+ if (ItemConstants.isRechargeable(ivItem.getItemId())) {
+ MapleInventoryManipulator.removeFromSlot(c, ivType, slot, ivItem.getQuantity(), true);
+ } else {
+ MapleInventoryManipulator.removeFromSlot(c, ivType, slot, (short) (bundles * perBundle), true);
+ }
+ } else if (mode == Action.REMOVE_ITEM.getCode()) {
+ if (isTradeOpen(chr)) return;
+
+ MaplePlayerShop shop = chr.getPlayerShop();
+ if (shop != null && shop.isOwner(chr)) {
+ if (shop.isOpen()) {
+ c.announce(MaplePacketCreator.serverNotice(1, "You can't take it with the store open."));
+ return;
+ }
+
+ int slot = slea.readShort();
+ if (slot >= shop.getItems().size() || slot < 0) {
+ AutobanFactory.PACKET_EDIT.alert(chr, chr.getName() + " tried to packet edit with a player shop.");
+ FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " tried to remove item at slot " + slot + "\r\n");
+ c.disconnect(true, false);
+ return;
+ }
+
+ shop.takeItemBack(slot, chr);
+ }
+ } else if (mode == Action.MERCHANT_MESO.getCode()) {
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if (merchant == null) return;
+
+ merchant.withdrawMesos(chr);
+ } else if (mode == Action.MERCHANT_ORGANIZE.getCode()) {
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if (merchant == null || !merchant.isOwner(chr)) return;
+
+ merchant.withdrawMesos(chr);
+ merchant.clearInexistentItems();
+
+ if (merchant.getItems().isEmpty()) {
+ merchant.closeOwnerMerchant(chr);
+ return;
+ }
+ c.announce(MaplePacketCreator.updateHiredMerchant(merchant, chr));
+
+ } else if (mode == Action.BUY.getCode() || mode == Action.MERCHANT_BUY.getCode()) {
+ if (isTradeOpen(chr)) return;
+
+ int itemid = slea.readByte();
+ short quantity = slea.readShort();
+ if (quantity < 1) {
+ AutobanFactory.PACKET_EDIT.alert(chr, chr.getName() + " tried to packet edit with a hired merchant and or player shop.");
+ FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " tried to buy item " + itemid + " with quantity " + quantity + "\r\n");
c.disconnect(true, false);
return;
}
-
- merchant.takeItemBack(slot, chr);
- }
- } else if (mode == Action.CLOSE_MERCHANT.getCode()) {
- if (isTradeOpen(chr)) return;
-
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if (merchant != null) {
- merchant.closeOwnerMerchant(chr);
- }
- } else if (mode == Action.MAINTENANCE_OFF.getCode()) {
- if (isTradeOpen(chr)) return;
-
- MapleHiredMerchant merchant = chr.getHiredMerchant();
- if(merchant != null) {
- if (merchant.getItems().isEmpty() && merchant.isOwner(chr)) {
- merchant.closeShop(c, false);
- chr.setHasMerchant(false);
+ MaplePlayerShop shop = chr.getPlayerShop();
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if (shop != null && shop.isVisitor(chr)) {
+ shop.buy(c, itemid, quantity);
+ shop.broadcast(MaplePacketCreator.getPlayerShopItemUpdate(shop));
+ } else if (merchant != null && !merchant.isOwner(chr)) {
+ merchant.buy(c, itemid, quantity);
+ merchant.broadcastToVisitorsThreadsafe(MaplePacketCreator.updateHiredMerchant(merchant, chr));
}
- if (merchant.isOwner(chr)) {
- merchant.clearMessages();
- merchant.setOpen(true);
- }
- }
-
- chr.setHiredMerchant(null);
- c.announce(MaplePacketCreator.enableActions());
- } else if (mode == Action.BAN_PLAYER.getCode()) {
- slea.skip(1);
-
- MaplePlayerShop shop = chr.getPlayerShop();
- if (shop != null && shop.isOwner(chr)) {
- shop.banPlayer(slea.readMapleAsciiString());
- }
- } else if (mode == Action.EXPEL.getCode()) {
- MapleMiniGame miniGame = chr.getMiniGame();
- if(miniGame != null && miniGame.isOwner(chr)) {
- MapleCharacter visitor = miniGame.getVisitor();
-
- if(visitor != null) {
- visitor.closeMiniGame();
- visitor.announce(MaplePacketCreator.getMiniGameClose(5));
+ } else if (mode == Action.TAKE_ITEM_BACK.getCode()) {
+ if (isTradeOpen(chr)) return;
+
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if (merchant != null && merchant.isOwner(chr)) {
+ if (merchant.isOpen()) {
+ c.announce(MaplePacketCreator.serverNotice(1, "You can't take it with the store open."));
+ return;
+ }
+
+ int slot = slea.readShort();
+ if (slot >= merchant.getItems().size() || slot < 0) {
+ AutobanFactory.PACKET_EDIT.alert(chr, chr.getName() + " tried to packet edit with a hired merchant.");
+ FilePrinter.printError(FilePrinter.EXPLOITS + chr.getName() + ".txt", chr.getName() + " tried to remove item at slot " + slot + "\r\n");
+ c.disconnect(true, false);
+ return;
+ }
+
+ merchant.takeItemBack(slot, chr);
+ }
+ } else if (mode == Action.CLOSE_MERCHANT.getCode()) {
+ if (isTradeOpen(chr)) return;
+
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if (merchant != null) {
+ merchant.closeOwnerMerchant(chr);
+ }
+ } else if (mode == Action.MAINTENANCE_OFF.getCode()) {
+ if (isTradeOpen(chr)) return;
+
+ MapleHiredMerchant merchant = chr.getHiredMerchant();
+ if(merchant != null) {
+ if (merchant.getItems().isEmpty() && merchant.isOwner(chr)) {
+ merchant.closeShop(c, false);
+ chr.setHasMerchant(false);
+ }
+ if (merchant.isOwner(chr)) {
+ merchant.clearMessages();
+ merchant.setOpen(true);
+ }
+ }
+
+ chr.setHiredMerchant(null);
+ c.announce(MaplePacketCreator.enableActions());
+ } else if (mode == Action.BAN_PLAYER.getCode()) {
+ slea.skip(1);
+
+ MaplePlayerShop shop = chr.getPlayerShop();
+ if (shop != null && shop.isOwner(chr)) {
+ shop.banPlayer(slea.readMapleAsciiString());
+ }
+ } else if (mode == Action.EXPEL.getCode()) {
+ MapleMiniGame miniGame = chr.getMiniGame();
+ if(miniGame != null && miniGame.isOwner(chr)) {
+ MapleCharacter visitor = miniGame.getVisitor();
+
+ if(visitor != null) {
+ visitor.closeMiniGame();
+ visitor.announce(MaplePacketCreator.getMiniGameClose(5));
+ }
}
}
+ } finally {
+ c.releaseClient();
}
}
diff --git a/src/net/server/channel/handlers/RangedAttackHandler.java b/src/net/server/channel/handlers/RangedAttackHandler.java
index 8bd367bc59..7d89da5ded 100644
--- a/src/net/server/channel/handlers/RangedAttackHandler.java
+++ b/src/net/server/channel/handlers/RangedAttackHandler.java
@@ -51,8 +51,8 @@ public final class RangedAttackHandler extends AbstractDealDamageHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleCharacter chr = c.getPlayer();
- chr.setPetLootCd(currentServerTime());
-
+ //chr.setPetLootCd(currentServerTime());
+
/*long timeElapsed = currentServerTime() - chr.getAutobanManager().getLastSpam(8);
if(timeElapsed < 300) {
AutobanFactory.FAST_ATTACK.alert(chr, "Time: " + timeElapsed);
diff --git a/src/net/server/channel/handlers/RingActionHandler.java b/src/net/server/channel/handlers/RingActionHandler.java
index e1d383dac4..aa1cf4828b 100644
--- a/src/net/server/channel/handlers/RingActionHandler.java
+++ b/src/net/server/channel/handlers/RingActionHandler.java
@@ -273,20 +273,20 @@ public final class RingActionHandler extends AbstractMaplePacketHandler {
}
public static void giveMarriageRings(MapleCharacter player, MapleCharacter partner, int marriageRingId) {
- int ringid = MapleRing.createRing(marriageRingId, player, partner);
+ Pair rings = MapleRing.createRing(marriageRingId, player, partner);
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
Item ringObj = ii.getEquipById(marriageRingId);
Equip ringEqp = (Equip) ringObj;
- ringEqp.setRingId(ringid);
- player.addMarriageRing(MapleRing.loadFromDb(ringid));
+ ringEqp.setRingId(rings.getLeft());
+ player.addMarriageRing(MapleRing.loadFromDb(rings.getLeft()));
MapleInventoryManipulator.addFromDrop(player.getClient(), ringEqp, false, -1);
player.broadcastMarriageMessage();
ringObj = ii.getEquipById(marriageRingId);
ringEqp = (Equip) ringObj;
- ringEqp.setRingId(ringid + 1);
- partner.addMarriageRing(MapleRing.loadFromDb(ringid + 1));
+ ringEqp.setRingId(rings.getRight());
+ partner.addMarriageRing(MapleRing.loadFromDb(rings.getRight()));
MapleInventoryManipulator.addFromDrop(partner.getClient(), ringEqp, false, -1);
partner.broadcastMarriageMessage();
}
@@ -513,6 +513,6 @@ public final class RingActionHandler extends AbstractMaplePacketHandler {
break;
}
- c.getSession().write(MaplePacketCreator.enableActions());
+ c.announce(MaplePacketCreator.enableActions());
}
}
diff --git a/src/net/server/channel/handlers/TouchingCashShopHandler.java b/src/net/server/channel/handlers/TouchingCashShopHandler.java
index 900a7a58fa..be241138b5 100644
--- a/src/net/server/channel/handlers/TouchingCashShopHandler.java
+++ b/src/net/server/channel/handlers/TouchingCashShopHandler.java
@@ -28,9 +28,10 @@ import tools.data.input.SeekableLittleEndianAccessor;
/**
*
- * @author Acrylic (Terry Han)
+ * @author Terry Han (Acrylic)
*/
public final class TouchingCashShopHandler extends AbstractMaplePacketHandler {
+ @Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
}
diff --git a/src/net/server/channel/handlers/UseCashItemHandler.java b/src/net/server/channel/handlers/UseCashItemHandler.java
index 18aeed4570..c678d48b19 100644
--- a/src/net/server/channel/handlers/UseCashItemHandler.java
+++ b/src/net/server/channel/handlers/UseCashItemHandler.java
@@ -276,7 +276,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
if (item == null) //hack
{
return;
- } else if (((item.getFlag() & ItemConstants.UNTRADEABLE) == ItemConstants.UNTRADEABLE) || (ii.isDropRestricted(item.getItemId()) && !MapleKarmaManipulator.hasKarmaFlag(item))) {
+ } else if (item.isUntradeable()) {
player.dropMessage(1, "You cannot trade this item.");
c.announce(MaplePacketCreator.enableActions());
return;
@@ -299,7 +299,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
break;
}
remove(c, itemId);
- } else if (itemType == 508) { // graduation banner, thanks to tmskdl12
+ } else if (itemType == 508) { // graduation banner, thanks to tmskdl12. Also, thanks ratency for first pointing lack of Kite handling
MapleKite kite = new MapleKite(player, slea.readMapleAsciiString(), itemId);
if (!GameConstants.isFreeMarketRoom(player.getMapId())) {
@@ -517,7 +517,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
Equip toScroll = (Equip) eitem;
if (toScroll.getUpgradeSlots() < 1) {
- c.getSession().write(MaplePacketCreator.getInventoryFull());
+ c.announce(MaplePacketCreator.getInventoryFull());
return;
}
@@ -527,10 +527,10 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
player.toggleBlockCashShop();
final int curlevel = toScroll.getLevel();
- c.getSession().write(MaplePacketCreator.sendVegaScroll(0x40));
+ c.announce(MaplePacketCreator.sendVegaScroll(0x40));
final Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, uitem.getItemId(), false, itemId, player.isGM());
- c.getSession().write(MaplePacketCreator.sendVegaScroll(scrolled.getLevel() > curlevel ? 0x41 : 0x43));
+ c.announce(MaplePacketCreator.sendVegaScroll(scrolled.getLevel() > curlevel ? 0x41 : 0x43));
//opcodes 0x42, 0x44: "this item cannot be used"; 0x39, 0x45: crashes
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, uSlot, (short) 1, false);
@@ -555,7 +555,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
player.equipChanged();
}
- client.getSession().write(MaplePacketCreator.enableActions());
+ client.announce(MaplePacketCreator.enableActions());
}
}, 1000 * 3);
} else {
diff --git a/src/net/server/channel/handlers/UseChairHandler.java b/src/net/server/channel/handlers/UseChairHandler.java
index d224c3208a..2b3989d3e7 100644
--- a/src/net/server/channel/handlers/UseChairHandler.java
+++ b/src/net/server/channel/handlers/UseChairHandler.java
@@ -24,7 +24,6 @@ package net.server.channel.handlers;
import client.MapleClient;
import client.inventory.MapleInventoryType;
import net.AbstractMaplePacketHandler;
-import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class UseChairHandler extends AbstractMaplePacketHandler {
@@ -34,8 +33,7 @@ public final class UseChairHandler extends AbstractMaplePacketHandler {
if (c.getPlayer().getInventory(MapleInventoryType.SETUP).findById(itemId) == null) {
return;
}
- c.getPlayer().setChair(itemId);
- c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showChair(c.getPlayer().getId(), itemId), false);
- c.announce(MaplePacketCreator.enableActions());
+
+ c.getPlayer().sitChair(itemId);
}
}
diff --git a/src/net/server/channel/handlers/UseMountFoodHandler.java b/src/net/server/channel/handlers/UseMountFoodHandler.java
index bf9c320435..c586c8defb 100644
--- a/src/net/server/channel/handlers/UseMountFoodHandler.java
+++ b/src/net/server/channel/handlers/UseMountFoodHandler.java
@@ -25,6 +25,7 @@ import client.MapleClient;
import client.MapleCharacter;
import client.MapleMount;
import client.inventory.Item;
+import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import constants.ExpTable;
import net.AbstractMaplePacketHandler;
@@ -45,29 +46,37 @@ public final class UseMountFoodHandler extends AbstractMaplePacketHandler {
MapleCharacter chr = c.getPlayer();
MapleMount mount = chr.getMount();
- Item item = chr.getInventory(MapleInventoryType.USE).getItem(pos);
- if (item != null && item.getItemId() == itemid && mount != null) {
- c.lockClient();
+ MapleInventory useInv = chr.getInventory(MapleInventoryType.USE);
+
+ if (c.tryacquireClient()) {
try {
- int curTiredness = mount.getTiredness();
- int healedTiredness = Math.min(curTiredness, 30);
-
- float healedFactor = (float) healedTiredness / 30;
- mount.setTiredness(curTiredness - healedTiredness);
-
- if (healedFactor > 0.0f) {
- mount.setExp(mount.getExp() + (int) Math.ceil(healedFactor * (2 * mount.getLevel() + 6)));
- int level = mount.getLevel();
- boolean levelup = mount.getExp() >= ExpTable.getMountExpNeededForLevel(level) && level < 31;
- if (levelup) {
- mount.setLevel(level + 1);
+ useInv.lockInventory();
+ try {
+ Item item = useInv.getItem(pos);
+ if (item != null && item.getItemId() == itemid && mount != null) {
+ int curTiredness = mount.getTiredness();
+ int healedTiredness = Math.min(curTiredness, 30);
+
+ float healedFactor = (float) healedTiredness / 30;
+ mount.setTiredness(curTiredness - healedTiredness);
+
+ if (healedFactor > 0.0f) {
+ mount.setExp(mount.getExp() + (int) Math.ceil(healedFactor * (2 * mount.getLevel() + 6)));
+ int level = mount.getLevel();
+ boolean levelup = mount.getExp() >= ExpTable.getMountExpNeededForLevel(level) && level < 31;
+ if (levelup) {
+ mount.setLevel(level + 1);
+ }
+ chr.getMap().broadcastMessage(MaplePacketCreator.updateMount(chr.getId(), mount, levelup));
+ }
+
+ MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, itemid, 1, true, false);
}
- chr.getMap().broadcastMessage(MaplePacketCreator.updateMount(chr.getId(), mount, levelup));
+ } finally {
+ useInv.unlockInventory();
}
-
- MapleInventoryManipulator.removeById(c, MapleInventoryType.USE, itemid, 1, true, false);
} finally {
- c.unlockClient();
+ c.releaseClient();
}
}
}
diff --git a/src/net/server/channel/handlers/WhisperHandler.java b/src/net/server/channel/handlers/WhisperHandler.java
index 90449fab77..02ded22631 100644
--- a/src/net/server/channel/handlers/WhisperHandler.java
+++ b/src/net/server/channel/handlers/WhisperHandler.java
@@ -49,9 +49,9 @@ public final class WhisperHandler extends AbstractMaplePacketHandler {
String recipient = slea.readMapleAsciiString();
String text = slea.readMapleAsciiString();
MapleCharacter player = c.getChannelServer().getPlayerStorage().getCharacterByName(recipient);
- if(c.getPlayer().getAutobanManager().getLastSpam(7) + 200 > currentServerTime()) {
- return;
- }
+ if (c.getPlayer().getAutobanManager().getLastSpam(7) + 200 > currentServerTime()) {
+ return;
+ }
if (text.length() > Byte.MAX_VALUE && !player.isGM()) {
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with whispers.");
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to send text with length of " + text.length() + "\r\n");
@@ -92,7 +92,7 @@ public final class WhisperHandler extends AbstractMaplePacketHandler {
} else {
c.announce(MaplePacketCreator.getFindReply(victim.getName(), victim.getMap().getId(), 1));
}
- } else if (c.getPlayer().gmLevel() > 1) { // not found
+ } else if (c.getPlayer().isGM()) { // not found
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT gm FROM characters WHERE name = ?");
diff --git a/src/net/server/handlers/login/LoginPasswordHandler.java b/src/net/server/handlers/login/LoginPasswordHandler.java
index 1feada78e3..a3963ee681 100644
--- a/src/net/server/handlers/login/LoginPasswordHandler.java
+++ b/src/net/server/handlers/login/LoginPasswordHandler.java
@@ -37,6 +37,9 @@ import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient;
import java.sql.ResultSet;
import java.sql.Statement;
+import java.io.UnsupportedEncodingException;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
public final class LoginPasswordHandler implements MaplePacketHandler {
@@ -45,6 +48,11 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
return !c.isLoggedIn();
}
+ private static String hashpwSHA512(String pwd) throws NoSuchAlgorithmException, UnsupportedEncodingException {
+ MessageDigest digester = MessageDigest.getInstance("SHA-512");
+ digester.update(pwd.getBytes("UTF-8"), 0, pwd.length());
+ return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase();
+ }
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
@@ -65,7 +73,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("INSERT INTO accounts (name, password, birthday, tempban) VALUES (?, ?, ?, ?);", Statement.RETURN_GENERATED_KEYS); //Jayd: Added birthday, tempban
ps.setString(1, login);
- ps.setString(2, BCrypt.hashpw(pwd, BCrypt.gensalt(12)));
+ ps.setString(2, ServerConstants.BCRYPT_MIGRATION ? BCrypt.hashpw(pwd, BCrypt.gensalt(12)) : hashpwSHA512(pwd));
ps.setString(3, "2018-06-20"); //Jayd's idea: was added to solve the MySQL 5.7 strict checking (birthday)
ps.setString(4, "2018-06-20"); //Jayd's idea: was added to solve the MySQL 5.7 strict checking (tempban)
ps.executeUpdate();
@@ -74,7 +82,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
rs.next();
c.setAccID(rs.getInt(1));
rs.close();
- } catch (SQLException e) {
+ } catch (SQLException | NoSuchAlgorithmException | UnsupportedEncodingException e) {
e.printStackTrace();
} finally {
disposeSql(con, ps);
diff --git a/src/scripting/AbstractPlayerInteraction.java b/src/scripting/AbstractPlayerInteraction.java
index 919c447d71..e63efc7dfa 100644
--- a/src/scripting/AbstractPlayerInteraction.java
+++ b/src/scripting/AbstractPlayerInteraction.java
@@ -682,8 +682,8 @@ public class AbstractPlayerInteraction {
public boolean isEventLeader() {
return getEventInstance() != null && getPlayer().getId() == getEventInstance().getLeaderId();
}
-
- public void givePartyItems(int id, short quantity, List party) {
+
+ public void givePartyItems(int id, short quantity, List party) {
for (MapleCharacter chr : party) {
MapleClient cl = chr.getClient();
if (quantity >= 0) {
@@ -695,7 +695,6 @@ public class AbstractPlayerInteraction {
}
}
-
public void removeHPQItems() {
int[] items = {4001095, 4001096, 4001097, 4001098, 4001099, 4001100, 4001101};
for (int i = 0; i < items.length; i ++) {
diff --git a/src/scripting/event/EventInstanceManager.java b/src/scripting/event/EventInstanceManager.java
index 5592d99d35..d1e599ea58 100644
--- a/src/scripting/event/EventInstanceManager.java
+++ b/src/scripting/event/EventInstanceManager.java
@@ -345,7 +345,7 @@ public class EventInstanceManager {
private void dismissEventTimer() {
for(MapleCharacter chr: getPlayers()) {
- chr.getClient().getSession().write(MaplePacketCreator.removeClock());
+ chr.getClient().announce(MaplePacketCreator.removeClock());
}
event_schedule = null;
diff --git a/src/server/CashShop.java b/src/server/CashShop.java
index d32991dc09..faa7df8bea 100644
--- a/src/server/CashShop.java
+++ b/src/server/CashShop.java
@@ -582,4 +582,9 @@ public class CashShop {
return null;
}
}
+
+ public static Item generateCouponItem(int itemId, short quantity) {
+ CashItem it = new CashItem(77777777, itemId, 7777, ItemConstants.isPet(itemId) ? 30 : 0, quantity, true);
+ return it.toItem();
+ }
}
diff --git a/src/server/MapleItemInformationProvider.java b/src/server/MapleItemInformationProvider.java
index 6c2929ccb8..241872282e 100644
--- a/src/server/MapleItemInformationProvider.java
+++ b/src/server/MapleItemInformationProvider.java
@@ -373,6 +373,7 @@ public class MapleItemInformationProvider {
private static short getExtraSlotMaxFromPlayer(MapleClient c, int itemId) {
short ret = 0;
+ // thanks GMChuck for detecting player sensitive data being cached into getSlotMax
if (ItemConstants.isThrowingStar(itemId)) {
if(c.getPlayer().getJob().isA(MapleJob.NIGHTWALKER1)) {
ret += c.getPlayer().getSkillLevel(SkillFactory.getSkill(NightWalker.CLAW_MASTERY)) * 10;
@@ -658,11 +659,11 @@ public class MapleItemInformationProvider {
}
private static short getMaximumShortMaxIfOverflow(int value1, int value2) {
- return (short)Math.min(Short.MAX_VALUE, Math.max(value1, value2));
+ return (short) Math.min(Short.MAX_VALUE, Math.max(value1, value2));
}
private static short getShortMaxIfOverflow(int value) {
- return (short)Math.min(Short.MAX_VALUE, value);
+ return (short) Math.min(Short.MAX_VALUE, value);
}
private static short chscrollRandomizedStat(int range) {
diff --git a/src/server/MapleShop.java b/src/server/MapleShop.java
index 18dab7152f..3364e36ee8 100644
--- a/src/server/MapleShop.java
+++ b/src/server/MapleShop.java
@@ -94,12 +94,12 @@ public class MapleShop {
if (c.getPlayer().getMeso() >= amount) {
if (MapleInventoryManipulator.checkSpace(c, itemId, quantity, "")) {
if (!ItemConstants.isRechargeable(itemId)) { //Pets can't be bought from shops
- MapleInventoryManipulator.addById(c, itemId, quantity);
+ MapleInventoryManipulator.addById(c, itemId, quantity, "", -1);
c.getPlayer().gainMeso(-amount, false);
} else {
short slotMax = ii.getSlotMax(c, item.getItemId());
quantity = slotMax;
- MapleInventoryManipulator.addById(c, itemId, quantity);
+ MapleInventoryManipulator.addById(c, itemId, quantity, "", -1);
c.getPlayer().gainMeso(-item.getPrice(), false);
}
c.announce(MaplePacketCreator.shopTransaction((byte) 0));
@@ -115,12 +115,12 @@ public class MapleShop {
if (c.getPlayer().getInventory(MapleInventoryType.ETC).countById(4310000) >= amount) {
if (MapleInventoryManipulator.checkSpace(c, itemId, quantity, "")) {
if (!ItemConstants.isRechargeable(itemId)) {
- MapleInventoryManipulator.addById(c, itemId, quantity);
+ MapleInventoryManipulator.addById(c, itemId, quantity, "", -1);
MapleInventoryManipulator.removeById(c, MapleInventoryType.ETC, 4310000, amount, false, false);
} else {
short slotMax = ii.getSlotMax(c, item.getItemId());
quantity = slotMax;
- MapleInventoryManipulator.addById(c, itemId, quantity);
+ MapleInventoryManipulator.addById(c, itemId, quantity, "", -1);
MapleInventoryManipulator.removeById(c, MapleInventoryType.ETC, 4310000, amount, false, false);
}
c.announce(MaplePacketCreator.shopTransaction((byte) 0));
@@ -138,9 +138,9 @@ public class MapleShop {
if (MapleInventoryManipulator.checkSpace(c, itemId, quantity, "")) {
if (ItemConstants.isPet(itemId)) {
int petid = MaplePet.createPet(itemId);
- MapleInventoryManipulator.addById(c, itemId, quantity, null, petid, -1);
+ MapleInventoryManipulator.addById(c, itemId, quantity, "", petid, -1);
} else {
- MapleInventoryManipulator.addById(c, itemId, quantity);
+ MapleInventoryManipulator.addById(c, itemId, quantity, "", -1, -1);
}
c.getPlayer().gainMeso(diff, false);
} else {
diff --git a/src/server/maps/MapleHiredMerchant.java b/src/server/maps/MapleHiredMerchant.java
index 1626ba0d5f..13d6113054 100644
--- a/src/server/maps/MapleHiredMerchant.java
+++ b/src/server/maps/MapleHiredMerchant.java
@@ -376,7 +376,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
if (mpsi.getItem().getInventoryType().equals(MapleInventoryType.EQUIP)) {
MapleInventoryManipulator.addFromDrop(c, mpsi.getItem(), false);
} else {
- MapleInventoryManipulator.addById(c, mpsi.getItem().getItemId(), (short) (mpsi.getBundles() * mpsi.getItem().getQuantity()), null, -1, mpsi.getItem().getFlag(), mpsi.getItem().getExpiration());
+ MapleInventoryManipulator.addById(c, mpsi.getItem().getItemId(), (short) (mpsi.getBundles() * mpsi.getItem().getQuantity()), mpsi.getItem().getOwner(), -1, mpsi.getItem().getFlag(), mpsi.getItem().getExpiration());
}
}
}
diff --git a/src/server/maps/MapleKite.java b/src/server/maps/MapleKite.java
index 5f210522d8..0ee937d625 100644
--- a/src/server/maps/MapleKite.java
+++ b/src/server/maps/MapleKite.java
@@ -43,12 +43,12 @@ public class MapleKite extends AbstractMapleMapObject {
@Override
public void sendDestroyData(MapleClient client) {
- client.getSession().write(makeDestroyData());
+ client.announce(makeDestroyData());
}
@Override
public void sendSpawnData(MapleClient client) {
- client.getSession().write(makeSpawnData());
+ client.announce(makeSpawnData());
}
public final byte[] makeSpawnData() {
diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java
index 66f1bc8678..04162be5db 100644
--- a/src/server/maps/MapleMap.java
+++ b/src/server/maps/MapleMap.java
@@ -3882,7 +3882,7 @@ public class MapleMap {
public void spawnHorntailOnGroundBelow(final Point targetPoint) { // ayy lmao
MapleMonster htIntro = MapleLifeFactory.getMonster(8810026);
- spawnMonsterOnGroundBelow(htIntro, targetPoint);
+ spawnMonsterOnGroundBelow(htIntro, targetPoint); // htintro spawn animation converting into horntail detected thanks to Arnah
final MapleMonster ht = MapleLifeFactory.getMonster(8810018);
ht.setParentMobOid(htIntro.getObjectId());
diff --git a/src/server/maps/MapleMapFactory.java b/src/server/maps/MapleMapFactory.java
index 9702f58cce..d959a3686d 100644
--- a/src/server/maps/MapleMapFactory.java
+++ b/src/server/maps/MapleMapFactory.java
@@ -427,21 +427,14 @@ public class MapleMapFactory {
public Map getMaps() {
mapsRLock.lock();
try {
- return Collections.unmodifiableMap(maps);
+ return new HashMap<>(maps);
} finally {
mapsRLock.unlock();
}
}
public void dispose() {
- Collection mapValues;
-
- mapsRLock.lock();
- try {
- mapValues = maps.values();
- } finally {
- mapsRLock.unlock();
- }
+ Collection mapValues = getMaps().values();
for(MapleMap map: mapValues) {
map.dispose();
diff --git a/src/server/maps/MapleMiniGame.java b/src/server/maps/MapleMiniGame.java
index 507cac4a56..499b76bf41 100644
--- a/src/server/maps/MapleMiniGame.java
+++ b/src/server/maps/MapleMiniGame.java
@@ -23,6 +23,7 @@ package server.maps;
import client.MapleCharacter;
import client.MapleClient;
+import net.server.Server;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -31,23 +32,25 @@ import tools.MaplePacketCreator;
/**
*
* @author Matze
- * @author Ronan (refactored String GameType to enum MiniGameType)
+ * @author Ronan (HeavenMS)
*/
public class MapleMiniGame extends AbstractMapleMapObject {
private MapleCharacter owner;
private MapleCharacter visitor;
private String password;
private MiniGameType GameType = MiniGameType.UNDEFINED;
+ private int piecetype;
+ private int inprogress = 0;
private int[] piece = new int[250];
private List list4x3 = new ArrayList<>();
private List list5x4 = new ArrayList<>();
private List list6x5 = new ArrayList<>();
private String description;
private int loser = 1;
- private int piecetype;
private int firstslot = 0;
- private int visitorpoints = 0;
- private int ownerpoints = 0;
+ private int visitorpoints = 0, visitorscore = 0, visitorforfeits = 0, lastvisitor = -1;
+ private int ownerpoints = 0, ownerscore = 0, ownerforfeits = 0;
+ private long nextavailabletie = 0;
private int matchestowin = 0;
public static enum MiniGameType {
@@ -91,12 +94,21 @@ public class MapleMiniGame extends AbstractMapleMapObject {
public void addVisitor(MapleCharacter challenger) {
visitor = challenger;
+ if (lastvisitor != challenger.getId()) {
+ ownerscore = 0;
+ ownerforfeits = 0;
+
+ visitorscore = 0;
+ visitorforfeits = 0;
+ lastvisitor = challenger.getId();
+ }
+
MapleCharacter owner = this.getOwner();
if (GameType == MiniGameType.OMOK) {
- owner.announce(MaplePacketCreator.getMiniGameNewVisitor(challenger, 1));
+ owner.announce(MaplePacketCreator.getMiniGameNewVisitor(this, challenger, 1));
owner.getMap().broadcastMessage(MaplePacketCreator.addOmokBox(owner, 2, 0));
} else if (GameType == MiniGameType.MATCH_CARD) {
- owner.announce(MaplePacketCreator.getMatchCardNewVisitor(challenger, 1));
+ owner.announce(MaplePacketCreator.getMatchCardNewVisitor(this, challenger, 1));
owner.getMap().broadcastMessage(MaplePacketCreator.addMatchCardBox(owner, 2, 0));
}
}
@@ -117,6 +129,13 @@ public class MapleMiniGame extends AbstractMapleMapObject {
return visitor == challenger;
}
+ public void broadcastToOwner(final byte[] packet) {
+ MapleClient c = owner.getClient();
+ if (c != null && c.getSession() != null) {
+ c.announce(packet);
+ }
+ }
+
public void broadcastToVisitor(final byte[] packet) {
if (visitor != null) {
visitor.getClient().announce(packet);
@@ -131,15 +150,83 @@ public class MapleMiniGame extends AbstractMapleMapObject {
return firstslot;
}
+ private void updateMiniGameBox() {
+ this.getOwner().getMap().broadcastMessage(MaplePacketCreator.addOmokBox(owner, visitor != null ? 2 : 1, inprogress));
+ }
+
+ private void minigameMatchFinished() {
+ inprogress = 0;
+ updateMiniGameBox();
+ }
+
+ public void minigameMatchStarted() {
+ inprogress = 1;
+ }
+
+ public boolean isMatchInProgress() {
+ return inprogress != 0;
+ }
+
+ public void denyTie(MapleCharacter chr) {
+ if (this.isOwner(chr)) {
+ inprogress |= (1 << 1);
+ } else {
+ inprogress |= (1 << 2);
+ }
+ }
+
+ public boolean isTieDenied(MapleCharacter chr) {
+ if (this.isOwner(chr)) {
+ return ((inprogress >> 1) % 2) == 1;
+ } else {
+ return ((inprogress >> 2) % 2) == 1;
+ }
+ }
+
+ public void minigameMatchOwnerWins(boolean forfeit) {
+ owner.setMiniGamePoints(visitor, 1, this.isOmok());
+ if (visitorforfeits < 4 || !forfeit) ownerscore += 50;
+ visitorscore += (15 * (forfeit ? -1 : 1));
+ if (forfeit) visitorforfeits++;
+
+ this.broadcast(MaplePacketCreator.getMiniGameOwnerWin(this, forfeit));
+ minigameMatchFinished();
+ }
+
+ public void minigameMatchVisitorWins(boolean forfeit) {
+ owner.setMiniGamePoints(visitor, 2, this.isOmok());
+ if (ownerforfeits < 4 || !forfeit) visitorscore += 50;
+ ownerscore += (15 * (forfeit ? -1 : 1));
+ if (forfeit) ownerforfeits++;
+
+ this.broadcast(MaplePacketCreator.getMiniGameVisitorWin(this, forfeit));
+ minigameMatchFinished();
+ }
+
+ public void minigameMatchDraw() {
+ owner.setMiniGamePoints(visitor, 3, this.isOmok());
+
+ long timeNow = Server.getInstance().getCurrentTime();
+ if (nextavailabletie <= timeNow) {
+ visitorscore += 10;
+ ownerscore += 10;
+
+ nextavailabletie = timeNow + 5 * 60 * 1000;
+ }
+
+ this.broadcast(MaplePacketCreator.getMiniGameTie(this));
+ minigameMatchFinished();
+ }
+
public void setOwnerPoints() {
ownerpoints++;
if (ownerpoints + visitorpoints == matchestowin) {
if (ownerpoints == visitorpoints) {
- this.broadcast(MaplePacketCreator.getMatchCardTie(this));
+ minigameMatchDraw();
} else if (ownerpoints > visitorpoints) {
- this.broadcast(MaplePacketCreator.getMatchCardOwnerWin(this));
+ minigameMatchOwnerWins(false);
} else {
- this.broadcast(MaplePacketCreator.getMatchCardVisitorWin(this));
+ minigameMatchVisitorWins(false);
}
ownerpoints = 0;
visitorpoints = 0;
@@ -150,11 +237,11 @@ public class MapleMiniGame extends AbstractMapleMapObject {
visitorpoints++;
if (ownerpoints + visitorpoints == matchestowin) {
if (ownerpoints > visitorpoints) {
- this.broadcast(MaplePacketCreator.getMiniGameOwnerWin(this));
+ minigameMatchOwnerWins(false);
} else if (visitorpoints > ownerpoints) {
- this.broadcast(MaplePacketCreator.getMiniGameVisitorWin(this));
+ minigameMatchVisitorWins(false);
} else {
- this.broadcast(MaplePacketCreator.getMiniGameTie(this));
+ minigameMatchDraw();
}
ownerpoints = 0;
visitorpoints = 0;
@@ -198,6 +285,10 @@ public class MapleMiniGame extends AbstractMapleMapObject {
public MiniGameType getGameType() {
return GameType;
}
+
+ public boolean isOmok() {
+ return GameType.equals(MiniGameType.OMOK);
+ }
public void shuffleList() {
if (matchestowin == 6) {
@@ -212,11 +303,11 @@ public class MapleMiniGame extends AbstractMapleMapObject {
public int getCardId(int slot) {
int cardid;
if (matchestowin == 6) {
- cardid = list4x3.get(slot - 1);
+ cardid = list4x3.get(slot);
} else if (matchestowin == 10) {
- cardid = list5x4.get(slot - 1);
+ cardid = list5x4.get(slot);
} else {
- cardid = list6x5.get(slot - 1);
+ cardid = list6x5.get(slot);
}
return cardid;
}
@@ -234,10 +325,7 @@ public class MapleMiniGame extends AbstractMapleMapObject {
}
public void broadcast(final byte[] packet) {
- MapleClient c = owner.getClient();
- if (c != null && c.getSession() != null) {
- c.announce(packet);
- }
+ broadcastToOwner(packet);
broadcastToVisitor(packet);
}
@@ -270,10 +358,10 @@ public class MapleMiniGame extends AbstractMapleMapObject {
for (int x = 0; x < 11; x++) {
if (searchCombo(x, y, type)) {
if (this.isOwner(chr)) {
- this.broadcast(MaplePacketCreator.getMiniGameOwnerWin(this));
+ this.minigameMatchOwnerWins(false);
this.setLoser(0);
} else {
- this.broadcast(MaplePacketCreator.getMiniGameVisitorWin(this));
+ this.minigameMatchVisitorWins(false);
this.setLoser(1);
}
for (int y2 = 0; y2 < 15; y2++) {
@@ -289,10 +377,10 @@ public class MapleMiniGame extends AbstractMapleMapObject {
for (int x = 4; x < 15; x++) {
if (searchCombo2(x, y, type)) {
if (this.isOwner(chr)) {
- this.broadcast(MaplePacketCreator.getMiniGameOwnerWin(this));
+ this.minigameMatchOwnerWins(false);
this.setLoser(0);
} else {
- this.broadcast(MaplePacketCreator.getMiniGameVisitorWin(this));
+ this.minigameMatchVisitorWins(false);
this.setLoser(1);
}
for (int y2 = 0; y2 < 15; y2++) {
@@ -351,14 +439,20 @@ public class MapleMiniGame extends AbstractMapleMapObject {
public String getDescription() {
return description;
}
-
- @Override
- public void sendDestroyData(MapleClient client) {
+
+ public int getOwnerScore() {
+ return ownerscore;
+ }
+
+ public int getVisitorScore() {
+ return visitorscore;
}
@Override
- public void sendSpawnData(MapleClient client) {
- }
+ public void sendDestroyData(MapleClient client) {}
+
+ @Override
+ public void sendSpawnData(MapleClient client) {}
@Override
public MapleMapObjectType getType() {
diff --git a/src/server/quest/MapleQuest.java b/src/server/quest/MapleQuest.java
index 6a99916b96..cf4ad35e01 100644
--- a/src/server/quest/MapleQuest.java
+++ b/src/server/quest/MapleQuest.java
@@ -354,7 +354,7 @@ public class MapleQuest {
newStatus.setCompletionTime(System.currentTimeMillis());
c.updateQuest(newStatus);
- c.getClient().getSession().write(MaplePacketCreator.showSpecialEffect(9)); // Quest completion
+ c.announce(MaplePacketCreator.showSpecialEffect(9)); // Quest completion
c.getMap().broadcastMessage(c, MaplePacketCreator.showForeignEffect(c.getId(), 9), false); //use 9 instead of 12 for both
return true;
}
diff --git a/src/server/quest/actions/ItemAction.java b/src/server/quest/actions/ItemAction.java
index b5b949d09f..1491515a98 100644
--- a/src/server/quest/actions/ItemAction.java
+++ b/src/server/quest/actions/ItemAction.java
@@ -149,7 +149,7 @@ public class ItemAction extends MapleQuestAction {
}
for(Pair iPair: giveItem) {
- MapleInventoryManipulator.addById(chr.getClient(), iPair.getLeft(), (short) iPair.getRight().shortValue());
+ MapleInventoryManipulator.addById(chr.getClient(), iPair.getLeft(), (short) iPair.getRight().shortValue(), "", -1);
chr.announce(MaplePacketCreator.getShowItemGain(iPair.getLeft(), (short) iPair.getRight().shortValue(), true));
}
}
diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java
index c8d1a8fd04..efd05534ea 100644
--- a/src/tools/MaplePacketCreator.java
+++ b/src/tools/MaplePacketCreator.java
@@ -1996,9 +1996,9 @@ public class MaplePacketCreator {
MapleMiniGame miniGame = chr.getMiniGame();
if (miniGame != null && miniGame.isOwner(chr)) {
if (miniGame.hasFreeSlot()) {
- spawnAnnounceBox(mplew, miniGame, 0, 1, 0);
+ addAnnounceBox(mplew, miniGame, 1, 0);
} else {
- spawnAnnounceBox(mplew, miniGame, 0, 2, 1);
+ addAnnounceBox(mplew, miniGame, 2, miniGame.isMatchInProgress() ? 1 : 0);
}
} else {
mplew.write(0);
@@ -2170,23 +2170,12 @@ public class MaplePacketCreator {
mplew.write(0);
}
- private static void addAnnounceBox(final MaplePacketLittleEndianWriter mplew, MapleMiniGame game, int type, int ammount, int joinable) {
- mplew.write(game.getGameType().getValue());
- mplew.writeInt(game.getObjectId()); // gameid/shopid
- mplew.writeMapleAsciiString(game.getDescription()); // desc
- mplew.writeMapleAsciiString(game.getPassword());
- mplew.write(type);
- mplew.write(ammount);
- mplew.write(2);
- mplew.write(joinable);
- }
-
- private static void spawnAnnounceBox(final MaplePacketLittleEndianWriter mplew, MapleMiniGame game, int type, int ammount, int joinable) {
+ private static void addAnnounceBox(final MaplePacketLittleEndianWriter mplew, MapleMiniGame game, int ammount, int joinable) {
mplew.write(game.getGameType().getValue());
mplew.writeInt(game.getObjectId()); // gameid/shopid
mplew.writeMapleAsciiString(game.getDescription()); // desc
mplew.writeBool(!game.getPassword().isEmpty()); // password here, thanks GabrielSin!
- mplew.write(type);
+ mplew.write(game.getPieceType());
mplew.write(ammount);
mplew.write(2); //player capacity
mplew.write(joinable);
@@ -5016,7 +5005,7 @@ public class MaplePacketCreator {
mplew.writeInt(minigame.getOwner().getMiniGamePoints(MiniGameResult.WIN, true));
mplew.writeInt(minigame.getOwner().getMiniGamePoints(MiniGameResult.TIE, true));
mplew.writeInt(minigame.getOwner().getMiniGamePoints(MiniGameResult.LOSS, true));
- mplew.writeInt(2000);
+ mplew.writeInt(minigame.getOwnerScore());
if (minigame.getVisitor() != null) {
MapleCharacter visitor = minigame.getVisitor();
mplew.write(1);
@@ -5024,7 +5013,7 @@ public class MaplePacketCreator {
mplew.writeInt(visitor.getMiniGamePoints(MiniGameResult.WIN, true));
mplew.writeInt(visitor.getMiniGamePoints(MiniGameResult.TIE, true));
mplew.writeInt(visitor.getMiniGamePoints(MiniGameResult.LOSS, true));
- mplew.writeInt(2000);
+ mplew.writeInt(minigame.getVisitorScore());
}
mplew.write(0xFF);
mplew.writeMapleAsciiString(minigame.getDescription());
@@ -5115,7 +5104,7 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
- public static byte[] getMiniGameNewVisitor(MapleCharacter c, int slot) {
+ public static byte[] getMiniGameNewVisitor(MapleMiniGame minigame, MapleCharacter c, int slot) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.VISIT.getCode());
@@ -5126,7 +5115,7 @@ public class MaplePacketCreator {
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.WIN, true));
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.TIE, true));
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.LOSS, true));
- mplew.writeInt(2000);
+ mplew.writeInt(minigame.getVisitorScore());
return mplew.getPacket();
}
@@ -5138,50 +5127,64 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
- private static byte[] getMiniGameResult(MapleMiniGame game, int win, int lose, int tie, int result, int forfeit, boolean omok) {
+ private static byte[] getMiniGameResult(MapleMiniGame game, int tie, int result, int forfeit) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.GET_RESULT.getCode());
+
+ int matchResultType;
if (tie == 0 && forfeit != 1) {
- mplew.write(0);
- } else if (tie == 1) {
- mplew.write(1);
- } else if (forfeit == 1) {
- mplew.write(2);
+ matchResultType = 0;
+ } else if (tie != 0) {
+ matchResultType = 1;
+ } else {
+ matchResultType = 2;
}
- mplew.write(0); // owner
- mplew.writeInt(1); // unknown
- mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.WIN, omok) + win); // wins
- mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.TIE, omok) + tie); // ties
- mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.LOSS, omok) + lose); // losses
- mplew.writeInt(2000); // points
- mplew.writeInt(1); // start of visitor; unknown
- mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.WIN, omok) + lose); // wins
- mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.TIE, omok) + tie); // ties
- mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.LOSS, omok) + win); // losses
- mplew.writeInt(2000); // points
- game.getOwner().setMiniGamePoints(game.getVisitor(), result, omok);
+
+ mplew.write(matchResultType);
+ mplew.writeBool(result == 2); // host/visitor wins
+
+ boolean omok = game.isOmok();
+ if (matchResultType == 1) {
+ mplew.write(0);
+ mplew.writeShort(0);
+ mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.WIN, omok)); // wins
+ mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.TIE, omok)); // ties
+ mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.LOSS, omok)); // losses
+ mplew.writeInt(game.getOwnerScore()); // points
+
+ mplew.writeInt(0); // unknown
+ mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.WIN, omok)); // wins
+ mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.TIE, omok)); // ties
+ mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.LOSS, omok)); // losses
+ mplew.writeInt(game.getVisitorScore()); // points
+ mplew.write(0);
+ } else {
+ mplew.writeInt(0);
+ mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.WIN, omok)); // wins
+ mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.TIE, omok)); // ties
+ mplew.writeInt(game.getOwner().getMiniGamePoints(MiniGameResult.LOSS, omok)); // losses
+ mplew.writeInt(game.getOwnerScore()); // points
+ mplew.writeInt(0);
+ mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.WIN, omok)); // wins
+ mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.TIE, omok)); // ties
+ mplew.writeInt(game.getVisitor().getMiniGamePoints(MiniGameResult.LOSS, omok)); // losses
+ mplew.writeInt(game.getVisitorScore()); // points
+ }
+
return mplew.getPacket();
}
- public static byte[] getMiniGameOwnerWin(MapleMiniGame game) {
- return getMiniGameResult(game, 0, 1, 0, 1, 0, true);
+ public static byte[] getMiniGameOwnerWin(MapleMiniGame game, boolean forfeit) {
+ return getMiniGameResult(game, 0, 1, forfeit ? 1 : 0);
}
- public static byte[] getMiniGameVisitorWin(MapleMiniGame game) {
- return getMiniGameResult(game, 1, 0, 0, 2, 0, true);
+ public static byte[] getMiniGameVisitorWin(MapleMiniGame game, boolean forfeit) {
+ return getMiniGameResult(game, 0, 2, forfeit ? 1 : 0);
}
public static byte[] getMiniGameTie(MapleMiniGame game) {
- return getMiniGameResult(game, 0, 0, 1, 3, 0, true);
- }
-
- public static byte[] getMiniGameOwnerForfeit(MapleMiniGame game) {
- return getMiniGameResult(game, 0, 1, 0, 2, 1, true);
- }
-
- public static byte[] getMiniGameVisitorForfeit(MapleMiniGame game) {
- return getMiniGameResult(game, 1, 0, 0, 1, 1, true);
+ return getMiniGameResult(game, 1, 3, 0);
}
public static byte[] getMiniGameClose(int type) {
@@ -5215,7 +5218,9 @@ public class MaplePacketCreator {
mplew.writeInt(minigame.getOwner().getMiniGamePoints(MiniGameResult.WIN, false));
mplew.writeInt(minigame.getOwner().getMiniGamePoints(MiniGameResult.TIE, false));
mplew.writeInt(minigame.getOwner().getMiniGamePoints(MiniGameResult.LOSS, false));
- mplew.writeInt(2000);
+
+ //set vs
+ mplew.writeInt(minigame.getOwnerScore());
if (minigame.getVisitor() != null) {
MapleCharacter visitor = minigame.getVisitor();
mplew.write(1);
@@ -5223,7 +5228,7 @@ public class MaplePacketCreator {
mplew.writeInt(visitor.getMiniGamePoints(MiniGameResult.WIN, false));
mplew.writeInt(visitor.getMiniGamePoints(MiniGameResult.TIE, false));
mplew.writeInt(visitor.getMiniGamePoints(MiniGameResult.LOSS, false));
- mplew.writeInt(2000);
+ mplew.writeInt(minigame.getVisitorScore());
}
mplew.write(0xFF);
mplew.writeMapleAsciiString(minigame.getDescription());
@@ -5237,20 +5242,24 @@ public class MaplePacketCreator {
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.START.getCode());
mplew.write(loser);
- mplew.write(0x0C);
- int last = 13;
+
+ int last;
if (game.getMatchesToWin() > 10) {
- last = 31;
+ last = 30;
} else if (game.getMatchesToWin() > 6) {
- last = 21;
+ last = 20;
+ } else {
+ last = 12;
}
- for (int i = 1; i < last; i++) {
+
+ mplew.write(last);
+ for (int i = 0; i < last; i++) {
mplew.writeInt(game.getCardId(i));
}
return mplew.getPacket();
}
- public static byte[] getMatchCardNewVisitor(MapleCharacter c, int slot) {
+ public static byte[] getMatchCardNewVisitor(MapleMiniGame minigame, MapleCharacter c, int slot) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.PLAYER_INTERACTION.getValue());
mplew.write(PlayerInteractionHandler.Action.VISIT.getCode());
@@ -5261,7 +5270,7 @@ public class MaplePacketCreator {
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.WIN, false));
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.TIE, false));
mplew.writeInt(c.getMiniGamePoints(MiniGameResult.LOSS, false));
- mplew.writeInt(2000);
+ mplew.writeInt(minigame.getVisitorScore());
return mplew.getPacket();
}
@@ -5280,18 +5289,6 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
- public static byte[] getMatchCardOwnerWin(MapleMiniGame game) {
- return getMiniGameResult(game, 1, 0, 0, 1, 0, false);
- }
-
- public static byte[] getMatchCardVisitorWin(MapleMiniGame game) {
- return getMiniGameResult(game, 0, 1, 0, 2, 0, false);
- }
-
- public static byte[] getMatchCardTie(MapleMiniGame game) {
- return getMiniGameResult(game, 0, 0, 1, 3, 0, false);
- }
-
public static byte[] fredrickMessage(byte operation) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.FREDRICK_MESSAGE.getValue());
@@ -5343,7 +5340,7 @@ public class MaplePacketCreator {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(c.getId());
- addAnnounceBox(mplew, c.getMiniGame(), 0, ammount, type);
+ addAnnounceBox(mplew, c.getMiniGame(), ammount, type);
return mplew.getPacket();
}
@@ -5351,7 +5348,7 @@ public class MaplePacketCreator {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.UPDATE_CHAR_BOX.getValue());
mplew.writeInt(c.getId());
- addAnnounceBox(mplew, c.getMiniGame(), 0, ammount, type);
+ addAnnounceBox(mplew, c.getMiniGame(), ammount, type);
return mplew.getPacket();
}
@@ -6005,7 +6002,7 @@ public class MaplePacketCreator {
mplew.write(new byte[]{-1, -1, -1, 0});
return mplew.getPacket();
}
-
+
public static byte[] showCouponRedeemedItem(int itemid) {
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
mplew.writeShort(SendOpcode.CASHSHOP_OPERATION.getValue());
@@ -6030,11 +6027,8 @@ public class MaplePacketCreator {
return mplew.getPacket();
}
- public static byte[] enableCSUse() {
- final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
- mplew.write(0x12);
- mplew.skip(6);
- return mplew.getPacket();
+ public static byte[] enableCSUse(MapleCharacter mc) {
+ return showCash(mc);
}
/**
diff --git a/tools/MapleCodeCouponGenerator/build.xml b/tools/MapleCodeCouponGenerator/build.xml
new file mode 100644
index 0000000000..785195515a
--- /dev/null
+++ b/tools/MapleCodeCouponGenerator/build.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project MapleCodeCouponGenerator.
+
+
+
diff --git a/tools/MapleCodeCouponGenerator/lib/CouponCodes.img.xml b/tools/MapleCodeCouponGenerator/lib/CouponCodes.img.xml
new file mode 100644
index 0000000000..9fccae135f
--- /dev/null
+++ b/tools/MapleCodeCouponGenerator/lib/CouponCodes.img.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tools/MapleCodeCouponGenerator/lib/mysql-connector-java-bin.jar b/tools/MapleCodeCouponGenerator/lib/mysql-connector-java-bin.jar
new file mode 100644
index 0000000000..0539039f71
Binary files /dev/null and b/tools/MapleCodeCouponGenerator/lib/mysql-connector-java-bin.jar differ
diff --git a/tools/MapleCodeCouponGenerator/manifest.mf b/tools/MapleCodeCouponGenerator/manifest.mf
new file mode 100644
index 0000000000..328e8e5bc3
--- /dev/null
+++ b/tools/MapleCodeCouponGenerator/manifest.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff --git a/tools/MapleCodeCouponGenerator/src/maplecodecoupongenerator/MapleCodeCouponGenerator.java b/tools/MapleCodeCouponGenerator/src/maplecodecoupongenerator/MapleCodeCouponGenerator.java
new file mode 100644
index 0000000000..0a63029697
--- /dev/null
+++ b/tools/MapleCodeCouponGenerator/src/maplecodecoupongenerator/MapleCodeCouponGenerator.java
@@ -0,0 +1,398 @@
+/*
+ This file is part of the HeavenMS MapleStory Server
+ Copyleft (L) 2016 - 2018 RonanLana
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package maplecodecoupongenerator;
+
+import java.io.BufferedReader;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+/**
+ *
+ * @author RonanLana
+
+ This application parses the coupon descriptor XML file and automatically generates
+ code entries on the DB reflecting the descriptions found. Parse time relies on the
+ sum of coupon codes created and amount of current codes on DB.
+
+ Estimated parse time: 2 minutes (for 100 code entries)
+ */
+public class MapleCodeCouponGenerator {
+ static String host = "jdbc:mysql://localhost:3306/heavenms";
+ static String driver = "com.mysql.jdbc.Driver";
+ static String username = "root";
+ static String password = "";
+
+ static Connection con = null;
+ static InputStreamReader fileReader = null;
+ static BufferedReader bufferedReader = null;
+
+ static String fileName = "lib/CouponCodes.img.xml";
+ static long currentTime;
+
+ static int initialStringLength = 250;
+
+ static String name;
+ static boolean active;
+ static int quantity, duration;
+ static int maplePoint, nxCredit, nxPrepaid;
+
+ static List> itemList = new ArrayList<>();
+ static Pair item;
+
+
+ static List activeCoupons = new ArrayList<>();
+ static List generatedKeys;
+ static Set usedCodes = new HashSet<>();
+
+ static byte status;
+
+ private static void resetCouponPackage() {
+ name = null;
+ active = false;
+ quantity = 1;
+ duration = 7;
+ maplePoint = 0;
+ nxCredit = 0;
+ nxPrepaid = 0;
+ itemList.clear();
+ }
+
+ private static String getName(String token) {
+ int i, j;
+ char[] dest;
+ String d;
+
+ i = token.lastIndexOf("name");
+ i = token.indexOf("\"", i) + 1; //lower bound of the string
+ j = token.indexOf("\"", i); //upper bound
+
+ dest = new char[initialStringLength];
+ try {
+ token.getChars(i, j, dest, 0);
+ } catch (StringIndexOutOfBoundsException e) {
+ // do nothing
+ return "";
+ } catch (Exception e) {
+ System.out.println("error in: " + token + "");
+ e.printStackTrace();
+ try {
+ Thread.sleep(100000000);
+ } catch (Exception ex) {}
+ }
+
+
+ d = new String(dest);
+ return(d.trim());
+ }
+
+ private static String getValue(String token) {
+ int i, j;
+ char[] dest;
+ String d;
+
+ i = token.lastIndexOf("value");
+ i = token.indexOf("\"", i) + 1; //lower bound of the string
+ j = token.indexOf("\"", i); //upper bound
+
+ dest = new char[initialStringLength];
+ token.getChars(i, j, dest, 0);
+
+ d = new String(dest);
+ return(d.trim());
+ }
+
+ private static void forwardCursor(int st) {
+ String line = null;
+
+ try {
+ while(status >= st && (line = bufferedReader.readLine()) != null) {
+ simpleToken(line);
+ }
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+ private static void simpleToken(String token) {
+ if(token.contains("/imgdir")) {
+ status -= 1;
+ }
+ else if(token.contains("imgdir")) {
+ status += 1;
+ }
+ }
+
+ private static void translateToken(String token) {
+ if(token.contains("/imgdir")) {
+ status -= 1;
+
+ if (status == 1) {
+ if (active) {
+ activeCoupons.add(new CodeCouponDescriptor(name, quantity, duration, maplePoint, nxCredit, nxPrepaid, itemList));
+ }
+
+ resetCouponPackage();
+ } else if (status == 3) {
+ itemList.add(item);
+ }
+ }
+ else if(token.contains("imgdir")) {
+ status += 1;
+
+ if (status == 4) {
+ item = new Pair<>(-1, -1);
+ } else if (status == 2) {
+ String d = getName(token);
+
+ System.out.println(" Reading coupon '" + d + "'");
+ name = d;
+ }
+ }
+ else {
+ String d = getName(token);
+
+ if (status == 2) {
+ switch (d) {
+ case "active":
+ if (Integer.valueOf(getValue(token)) == 0) {
+ forwardCursor(status);
+ resetCouponPackage();
+ } else {
+ active = true;
+ }
+ break;
+
+ case "quantity":
+ quantity = Integer.valueOf(getValue(token));
+ break;
+ case "duration":
+ duration = Integer.valueOf(getValue(token));
+ break;
+ case "maplePoint":
+ maplePoint = Integer.valueOf(getValue(token));
+ break;
+ case "nxCredit":
+ nxCredit = Integer.valueOf(getValue(token));
+ break;
+ case "nxPrepaid":
+ nxPrepaid = Integer.valueOf(getValue(token));
+ break;
+ }
+ } else if (status == 4) {
+ switch (d) {
+ case "count":
+ item.right = Integer.valueOf(getValue(token));
+ break;
+ case "id":
+ item.left = Integer.valueOf(getValue(token));
+ break;
+ }
+ }
+ }
+ }
+
+ private static class CodeCouponDescriptor {
+ protected String name;
+ protected int quantity, duration;
+ protected int nxCredit, maplePoint, nxPrepaid;
+ protected List> itemList;
+
+ protected CodeCouponDescriptor(String name, int quantity, int duration, int maplePoint, int nxCredit, int nxPrepaid, List> itemList) {
+ this.name = name;
+ this.quantity = quantity;
+ this.duration = duration;
+ this.maplePoint = maplePoint;
+ this.nxCredit = nxCredit;
+ this.nxPrepaid = nxPrepaid;
+
+ this.itemList = new ArrayList<>(itemList);
+ }
+ }
+
+ private static String randomizeCouponCode() {
+ StringBuilder rnd = new StringBuilder(Long.toHexString(Double.doubleToLongBits(Math.random())));
+ rnd.setCharAt(5, '-');
+ rnd.insert(11, '-');
+ return rnd.toString();
+ }
+
+ private static String generateCouponCode() {
+ String newCode;
+ do {
+ newCode = randomizeCouponCode();
+ } while (usedCodes.contains(newCode));
+
+ usedCodes.add(newCode);
+ return newCode;
+ }
+
+ private static List getGeneratedKeys(PreparedStatement ps) throws SQLException {
+ if (generatedKeys == null) {
+ generatedKeys = new ArrayList<>();
+
+ ResultSet rs = ps.getGeneratedKeys();
+ while (rs.next()) {
+ generatedKeys.add(rs.getInt(1));
+ }
+ rs.close();
+ }
+
+ return generatedKeys;
+ }
+
+ private static void commitCodeCouponDescription(CodeCouponDescriptor recipe) throws SQLException {
+ if (recipe.quantity < 1) return;
+
+ System.out.println(" Generating coupon '" + recipe.name + "'");
+ generatedKeys = null;
+
+ PreparedStatement ps = con.prepareStatement("INSERT IGNORE INTO `nxcode` (`code`, `expiration`) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS);
+ ps.setLong(2, currentTime + (recipe.duration * 60 * 60 * 1000));
+
+ for(int i = 0; i < recipe.quantity; i++) {
+ ps.setString(1, generateCouponCode());
+ ps.addBatch();
+ }
+ ps.executeBatch();
+
+ PreparedStatement ps2 = con.prepareStatement("INSERT IGNORE INTO `nxcode_items` (`codeid`, `type`, `item`, `quantity`) VALUES (?, ?, ?, ?)");
+ if (!recipe.itemList.isEmpty()) {
+ ps2.setInt(2, 5);
+ List keys = getGeneratedKeys(ps);
+
+ for (Pair p : recipe.itemList) {
+ ps2.setInt(3, p.getLeft());
+ ps2.setInt(4, p.getRight());
+
+ for (Integer codeid : keys) {
+ ps2.setInt(1, codeid);
+ ps2.addBatch();
+ }
+ }
+ }
+
+ ps2.setInt(4, 0);
+ if (recipe.nxCredit > 0) {
+ ps2.setInt(2, 0);
+ ps2.setInt(3, recipe.nxCredit);
+ List keys = getGeneratedKeys(ps);
+
+ for(Integer codeid : keys) {
+ ps2.setInt(1, codeid);
+ ps2.addBatch();
+ }
+ }
+
+ if (recipe.maplePoint > 0) {
+ ps2.setInt(2, 1);
+ ps2.setInt(3, recipe.maplePoint);
+ List keys = getGeneratedKeys(ps);
+
+ for(Integer codeid : keys) {
+ ps2.setInt(1, codeid);
+ ps2.addBatch();
+ }
+ }
+
+ if (recipe.nxPrepaid > 0) {
+ ps2.setInt(2, 2);
+ ps2.setInt(3, recipe.nxPrepaid);
+ List keys = getGeneratedKeys(ps);
+
+ for(Integer codeid : keys) {
+ ps2.setInt(1, codeid);
+ ps2.addBatch();
+ }
+ }
+
+ ps2.executeBatch();
+ ps2.close();
+ ps.close();
+ }
+
+ private static void loadUsedCouponCodes() throws SQLException {
+ PreparedStatement ps = con.prepareStatement("SELECT code FROM nxcode", Statement.RETURN_GENERATED_KEYS);
+ ResultSet rs = ps.executeQuery();
+ while (rs.next()) {
+ usedCodes.add(rs.getString("code"));
+ }
+ rs.close();
+ ps.close();
+ }
+
+ private static void generateCodeCoupons(String fileName) throws IOException {
+ fileReader = new InputStreamReader(new FileInputStream(fileName), "UTF-8");
+ bufferedReader = new BufferedReader(fileReader);
+
+ resetCouponPackage();
+ status = 0;
+
+ System.out.println("Reading XML coupon information...");
+ String line;
+ while((line = bufferedReader.readLine()) != null) {
+ translateToken(line);
+ }
+
+ bufferedReader.close();
+ fileReader.close();
+ System.out.println();
+
+ try {
+ Class.forName(driver).newInstance();
+ con = DriverManager.getConnection(host, username, password);
+
+ System.out.println("Loading DB coupon codes...");
+ loadUsedCouponCodes();
+ System.out.println();
+
+ System.out.println("Saving generated coupons...");
+ currentTime = System.currentTimeMillis();
+ for (CodeCouponDescriptor ccd : activeCoupons) {
+ commitCodeCouponDescription(ccd);
+ }
+ System.out.println();
+
+ con.close();
+ System.out.println("Done.");
+ } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | SQLException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static void main(String[] args) {
+ try {
+ generateCodeCoupons(fileName);
+ } catch(IOException ex) {
+ System.out.println("Error reading file '" + fileName + "'");
+ }
+ }
+}
diff --git a/tools/MapleCodeCouponGenerator/src/maplecodecoupongenerator/Pair.java b/tools/MapleCodeCouponGenerator/src/maplecodecoupongenerator/Pair.java
new file mode 100644
index 0000000000..3a8abd3b16
--- /dev/null
+++ b/tools/MapleCodeCouponGenerator/src/maplecodecoupongenerator/Pair.java
@@ -0,0 +1,121 @@
+/*
+This file is part of the OdinMS Maple Story Server
+Copyright (C) 2008 ~ 2010 Patrick Huy
+Matthias Butz
+Jan Christian Meyer
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License version 3
+as published by the Free Software Foundation. You may not use, modify
+or distribute this program under any other version of the
+GNU Affero General Public License.
+
+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 Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+ */
+package maplecodecoupongenerator;
+
+/**
+ * Represents a pair of values.
+ *
+ * @author Frz
+ * @since Revision 333
+ * @version 1.0
+ *
+ * @param The type of the left value.
+ * @param The type of the right value.
+ */
+public class Pair {
+
+ public E left;
+ public F right;
+
+ /**
+ * Class constructor - pairs two objects together.
+ *
+ * @param left The left object.
+ * @param right The right object.
+ */
+ public Pair(E left, F right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ /**
+ * Gets the left value.
+ *
+ * @return The left value.
+ */
+ public E getLeft() {
+ return left;
+ }
+
+ /**
+ * Gets the right value.
+ *
+ * @return The right value.
+ */
+ public F getRight() {
+ return right;
+ }
+
+ /**
+ * Turns the pair into a string.
+ *
+ * @return Each value of the pair as a string joined by a colon.
+ */
+ @Override
+ public String toString() {
+ return left.toString() + ":" + right.toString();
+ }
+
+ /**
+ * Gets the hash code of this pair.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((left == null) ? 0 : left.hashCode());
+ result = prime * result + ((right == null) ? 0 : right.hashCode());
+ return result;
+ }
+
+ /**
+ * Checks to see if two pairs are equal.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Pair other = (Pair) obj;
+ if (left == null) {
+ if (other.left != null) {
+ return false;
+ }
+ } else if (!left.equals(other.left)) {
+ return false;
+ }
+ if (right == null) {
+ if (other.right != null) {
+ return false;
+ }
+ } else if (!right.equals(other.right)) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/tools/MapleDojoUpdater/src/mapledojoupdate/MapleDojoUpdate.java b/tools/MapleDojoUpdater/src/mapledojoupdate/MapleDojoUpdate.java
index 2cb8da3365..ab1d051324 100644
--- a/tools/MapleDojoUpdater/src/mapledojoupdate/MapleDojoUpdate.java
+++ b/tools/MapleDojoUpdater/src/mapledojoupdate/MapleDojoUpdate.java
@@ -204,9 +204,6 @@ public class MapleDojoUpdate {
}
}
- /**
- * @param args the command line arguments
- */
public static void main(String[] args) {
parseDirectoryDojoData("");
}
diff --git a/tools/MapleEquipmentOmnileveler/src/mapleequipmentomnileveler/MapleEquipmentOmnileveler.java b/tools/MapleEquipmentOmnileveler/src/mapleequipmentomnileveler/MapleEquipmentOmnileveler.java
index 81e6602368..7241bb5e53 100644
--- a/tools/MapleEquipmentOmnileveler/src/mapleequipmentomnileveler/MapleEquipmentOmnileveler.java
+++ b/tools/MapleEquipmentOmnileveler/src/mapleequipmentomnileveler/MapleEquipmentOmnileveler.java
@@ -485,9 +485,6 @@ public class MapleEquipmentOmnileveler {
}
}
- /**
- * @param args the command line arguments
- */
public static void main(String[] args) {
parseDirectoryEquipData("");
}
diff --git a/tools/MapleMobBookUpdate/nbproject/project.properties b/tools/MapleMobBookUpdate/nbproject/project.properties
index 261b9554ca..e3c4fff9e1 100644
--- a/tools/MapleMobBookUpdate/nbproject/project.properties
+++ b/tools/MapleMobBookUpdate/nbproject/project.properties
@@ -29,12 +29,10 @@ dist.jar=${dist.dir}/MapleMobBookUpdate.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
-file.reference.MobBookUpdate-lib=lib
-file.reference.mysql-connector-java-bin.jar=lib/mysql-connector-java-bin.jar
+file.reference.mysql-connector-java-bin.jar=lib\\mysql-connector-java-bin.jar
includes=**
jar.compress=false
javac.classpath=\
- ${file.reference.MobBookUpdate-lib}:\
${file.reference.mysql-connector-java-bin.jar}
# Space-separated list of extra javac options
javac.compilerargs=
@@ -68,8 +66,7 @@ mkdist.disabled=false
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
- ${build.classes.dir}:\
- ${file.reference.mysql-connector-java-bin.jar}
+ ${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
diff --git a/tools/MapleQuestItemCountFetcher/lib/commons-io-2.6.jar b/tools/MapleQuestItemCountFetcher/lib/commons-io-2.6.jar
new file mode 100644
index 0000000000..00556b119d
Binary files /dev/null and b/tools/MapleQuestItemCountFetcher/lib/commons-io-2.6.jar differ
diff --git a/tools/MapleQuestItemCountFetcher/nbproject/project.properties b/tools/MapleQuestItemCountFetcher/nbproject/project.properties
index f2ee825b49..f4bf11fcfe 100644
--- a/tools/MapleQuestItemCountFetcher/nbproject/project.properties
+++ b/tools/MapleQuestItemCountFetcher/nbproject/project.properties
@@ -30,11 +30,9 @@ dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.commons-io-2.6.jar=lib/commons-io-2.6.jar
-file.reference.mysql-connector-java-bin.jar=../../cores/mysql-connector-java-bin.jar
includes=**
jar.compress=false
javac.classpath=\
- ${file.reference.mysql-connector-java-bin.jar}:\
${file.reference.commons-io-2.6.jar}
# Space-separated list of extra javac options
javac.compilerargs=
diff --git a/tools/MapleQuestItemCountFetcher/src/maplequestitemcountfetcher/MapleQuestItemCountFetcher.java b/tools/MapleQuestItemCountFetcher/src/maplequestitemcountfetcher/MapleQuestItemCountFetcher.java
index 9e4795f27d..0b135b562a 100644
--- a/tools/MapleQuestItemCountFetcher/src/maplequestitemcountfetcher/MapleQuestItemCountFetcher.java
+++ b/tools/MapleQuestItemCountFetcher/src/maplequestitemcountfetcher/MapleQuestItemCountFetcher.java
@@ -25,7 +25,6 @@ import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
-import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;
import java.util.LinkedHashMap;
@@ -47,10 +46,8 @@ import java.util.Map.Entry;
public class MapleQuestItemCountFetcher {
static String actName = "../../wz/Quest.wz/Act.img.xml";
static String checkName = "../../wz/Quest.wz/Check.img.xml";
- static String directoryName = "../..";
static String newFile = "lib/QuestReport.txt";
-
- static Connection con = null;
+
static PrintWriter printWriter = null;
static InputStreamReader fileReader = null;
static BufferedReader bufferedReader = null;
diff --git a/tools/SpiderDropFetcher/build.xml b/tools/SpiderDropFetcher/build.xml
new file mode 100644
index 0000000000..69ac122190
--- /dev/null
+++ b/tools/SpiderDropFetcher/build.xml
@@ -0,0 +1,73 @@
+
+
+
+
+
+
+
+
+
+
+ Builds, tests, and runs the project SpiderDropFetcher.
+
+
+
diff --git a/tools/SpiderDropFetcher/manifest.mf b/tools/SpiderDropFetcher/manifest.mf
new file mode 100644
index 0000000000..328e8e5bc3
--- /dev/null
+++ b/tools/SpiderDropFetcher/manifest.mf
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+X-COMMENT: Main-Class will be added automatically by build
+
diff --git a/tools/SpiderDropFetcher/src/client/inventory/MapleInventoryType.java b/tools/SpiderDropFetcher/src/client/inventory/MapleInventoryType.java
new file mode 100644
index 0000000000..778262f463
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/client/inventory/MapleInventoryType.java
@@ -0,0 +1,73 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package client.inventory;
+
+/**
+ * @author Matze
+ */
+public enum MapleInventoryType {
+ UNDEFINED(0),
+ EQUIP(1),
+ USE(2),
+ SETUP(3),
+ ETC(4),
+ CASH(5),
+ CANHOLD(6), //Proof-guard for inserting after removal checks
+ EQUIPPED(-1); //Seems nexon screwed something when removing an item T_T
+ final byte type;
+
+ private MapleInventoryType(int type) {
+ this.type = (byte) type;
+ }
+
+ public byte getType() {
+ return type;
+ }
+
+ public short getBitfieldEncoding() {
+ return (short) (2 << type);
+ }
+
+ public static MapleInventoryType getByType(byte type) {
+ for (MapleInventoryType l : MapleInventoryType.values()) {
+ if (l.getType() == type) {
+ return l;
+ }
+ }
+ return null;
+ }
+
+ public static MapleInventoryType getByWZName(String name) {
+ if (name.equals("Install")) {
+ return SETUP;
+ } else if (name.equals("Consume")) {
+ return USE;
+ } else if (name.equals("Etc")) {
+ return ETC;
+ } else if (name.equals("Cash")) {
+ return CASH;
+ } else if (name.equals("Pet")) {
+ return CASH;
+ }
+ return UNDEFINED;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/constants/CharsetConstants.java b/tools/SpiderDropFetcher/src/constants/CharsetConstants.java
new file mode 100644
index 0000000000..a9a3cd6973
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/constants/CharsetConstants.java
@@ -0,0 +1,48 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+
+package constants;
+
+/*
+ * Courtesy of GabrielSin (gabrielsin@playellin.net)
+ * Ellin
+ * MapleStory Server
+ * CharsetConstants
+ */
+
+public class CharsetConstants {
+
+ public static MapleLanguageType MAPLE_TYPE = MapleLanguageType.LANGUAGE_PT_BR;
+
+ public enum MapleLanguageType {
+ LANGUAGE_PT_BR(1, "ISO-8859-1"),
+ LANGUAGE_US(2, "US-ASCII");
+ final byte type;
+ final String ascii;
+
+ private MapleLanguageType(int type, String ascii) {
+ this.type = (byte) type;
+ this.ascii = ascii;
+ }
+
+ public String getAscii() {
+ return ascii;
+ }
+
+ public byte getType() {
+ return type;
+ }
+
+ public static MapleLanguageType getByType(byte type) {
+ for (MapleLanguageType l : MapleLanguageType.values()) {
+ if (l.getType() == type) {
+ return l;
+ }
+ }
+ return LANGUAGE_PT_BR;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/SpiderDropFetcher/src/constants/ItemConstants.java b/tools/SpiderDropFetcher/src/constants/ItemConstants.java
new file mode 100644
index 0000000000..860c392f34
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/constants/ItemConstants.java
@@ -0,0 +1,234 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package constants;
+
+import client.inventory.MapleInventoryType;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ *
+ * @author Jay Estrella
+ * @author Ronan
+ */
+public final class ItemConstants {
+ protected static Map inventoryTypeCache = new HashMap<>();
+
+ public final static int LOCK = 0x01;
+ public final static int SPIKES = 0x02;
+ public final static int KARMA_USE = 0x02;
+ public final static int COLD = 0x04;
+ public final static int UNTRADEABLE = 0x08;
+ public final static int KARMA_EQP = 0x10;
+ public final static int KARMA_UNTRADEABLE = 0x20; // let 0x20 until it's proven something uses this
+ public final static int SANDBOX = 0x40; // let 0x40 until it's proven something uses this
+ public final static int PET_COME = 0x80;
+ public final static int ACCOUNT_SHARING = 0x100;
+
+ public final static boolean EXPIRING_ITEMS = true;
+ public final static Set permanentItemids = new HashSet<>();
+
+ static {
+ int[] pi = {5000060, 5000100, 5000101, 5000102}; // i ain't going to open one gigantic itemid cache just for 4 perma itemids, no way!
+ for(int i : pi) {
+ permanentItemids.add(i);
+ }
+ }
+
+ public static int getFlagByInt(int type) {
+ if (type == 128) {
+ return PET_COME;
+ } else if (type == 256) {
+ return ACCOUNT_SHARING;
+ }
+ return 0;
+ }
+
+ public static boolean isThrowingStar(int itemId) {
+ return itemId / 10000 == 207;
+ }
+
+ public static boolean isBullet(int itemId) {
+ return itemId / 10000 == 233;
+ }
+
+ public static boolean isPotion(int itemId) {
+ return itemId / 1000 == 2000;
+ }
+
+ public static boolean isFood(int itemId) {
+ int useType = itemId / 1000;
+ return useType == 2022 || useType == 2010 || useType == 2020;
+ }
+
+ public static boolean isConsumable(int itemId) {
+ return isPotion(itemId) || isFood(itemId);
+ }
+
+ public static boolean isRechargeable(int itemId) {
+ return isThrowingStar(itemId) || isBullet(itemId);
+ }
+
+ public static boolean isArrowForCrossBow(int itemId) {
+ return itemId / 1000 == 2061;
+ }
+
+ public static boolean isArrowForBow(int itemId) {
+ return itemId / 1000 == 2060;
+ }
+
+ public static boolean isArrow(int itemId) {
+ return isArrowForBow(itemId) || isArrowForCrossBow(itemId);
+ }
+
+ public static boolean isPet(int itemId) {
+ return itemId / 1000 == 5000;
+ }
+
+ public static boolean isExpirablePet(int itemId) {
+ return itemId == 5000054;
+ }
+
+ public static boolean isPermanentItem(int itemId) {
+ return permanentItemids.contains(itemId);
+ }
+
+ public static boolean isNewYearCardEtc(int itemId) {
+ return itemId / 10000 == 430;
+ }
+
+ public static boolean isNewYearCardUse(int itemId) {
+ return itemId / 10000 == 216;
+ }
+
+ public static boolean isAccessory(int itemId) {
+ return itemId >= 1110000 && itemId < 1140000;
+ }
+
+ public static boolean isTaming(int itemId) {
+ int itemType = itemId / 1000;
+ return itemType == 1902 || itemType == 1912;
+ }
+
+ public static boolean isTownScroll(int itemId) {
+ return itemId >= 2030000 && itemId < 2030100;
+ }
+
+ public static boolean isAntibanishScroll(int itemId) {
+ return itemId == 2030100;
+ }
+
+ public static boolean isCleanSlate(int scrollId) {
+ return scrollId > 2048999 && scrollId < 2049004;
+ }
+
+ public static boolean isFlagModifier(int scrollId, byte flag) {
+ if(scrollId == 2041058 && ((flag & ItemConstants.COLD) == ItemConstants.COLD)) return true;
+ if(scrollId == 2040727 && ((flag & ItemConstants.SPIKES) == ItemConstants.SPIKES)) return true;
+ return false;
+ }
+
+ public static boolean isChaosScroll(int scrollId) {
+ return scrollId >= 2049100 && scrollId <= 2049103;
+ }
+
+ public static boolean isRateCoupon(int itemId) {
+ int itemType = itemId / 1000;
+ return itemType == 5211 || itemType == 5360;
+ }
+
+ public static boolean isExpCoupon(int couponId) {
+ return couponId / 1000 == 5211;
+ }
+
+ public static boolean isPartyItem(int itemId) {
+ return itemId >= 2022430 && itemId <= 2022433;
+ }
+
+ public static boolean isPartyAllcure(int itemId) {
+ return itemId == 2022433;
+ }
+
+ public static boolean isHiredMerchant(int itemId) {
+ return itemId / 10000 == 503;
+ }
+
+ public static boolean isPlayerShop(int itemId) {
+ return itemId / 10000 == 514;
+ }
+
+ public static MapleInventoryType getInventoryType(final int itemId) {
+ if (inventoryTypeCache.containsKey(itemId)) {
+ return inventoryTypeCache.get(itemId);
+ }
+
+ MapleInventoryType ret = MapleInventoryType.UNDEFINED;
+
+ final byte type = (byte) (itemId / 1000000);
+ if (type >= 1 && type <= 5) {
+ ret = MapleInventoryType.getByType(type);
+ }
+
+ inventoryTypeCache.put(itemId, ret);
+ return ret;
+ }
+
+ public static boolean isMakerReagent(int itemId) {
+ return itemId / 10000 == 425;
+ }
+
+ public static boolean isOverall(int itemId) {
+ return itemId / 10000 == 105;
+ }
+
+ public static boolean isCashStore(int itemId) {
+ int itemType = itemId / 10000;
+ return itemType == 503 || itemType == 514;
+ }
+
+ public static boolean isMapleLife(int itemId) {
+ int itemType = itemId / 10000;
+ return itemType == 543 && itemId != 5430000;
+ }
+
+ public static boolean isWeapon(int itemId) {
+ return itemId >= 1302000 && itemId < 1492024;
+ }
+
+ public static boolean isEquipment(int itemId) {
+ return itemId < 2000000 && itemId != 0;
+ }
+
+ public static boolean isMedal(int itemId) {
+ return itemId >= 1140000 && itemId < 1143000;
+ }
+
+ public static boolean isWeddingRing(int itemId) {
+ return itemId >= 1112803 && itemId <= 1112809;
+ }
+
+ public static boolean isWeddingToken(int itemId) {
+ return itemId >= 4031357 && itemId <= 4031364;
+ }
+}
diff --git a/src/tools/dropspider/DataTool.java b/tools/SpiderDropFetcher/src/dropspider/DataTool.java
similarity index 99%
rename from src/tools/dropspider/DataTool.java
rename to tools/SpiderDropFetcher/src/dropspider/DataTool.java
index b412da3c69..cb71379b82 100644
--- a/src/tools/dropspider/DataTool.java
+++ b/tools/SpiderDropFetcher/src/dropspider/DataTool.java
@@ -2,7 +2,7 @@
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
-package tools.dropspider;
+package dropspider;
import java.io.File;
import java.util.ArrayList;
diff --git a/src/tools/dropspider/DropEntry.java b/tools/SpiderDropFetcher/src/dropspider/DropEntry.java
similarity index 99%
rename from src/tools/dropspider/DropEntry.java
rename to tools/SpiderDropFetcher/src/dropspider/DropEntry.java
index 63caf8f9f9..9dc5457d87 100644
--- a/src/tools/dropspider/DropEntry.java
+++ b/tools/SpiderDropFetcher/src/dropspider/DropEntry.java
@@ -2,7 +2,7 @@
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
-package tools.dropspider;
+package dropspider;
import client.inventory.MapleInventoryType;
import constants.ItemConstants;
diff --git a/src/tools/dropspider/Errors.java b/tools/SpiderDropFetcher/src/dropspider/Errors.java
similarity index 93%
rename from src/tools/dropspider/Errors.java
rename to tools/SpiderDropFetcher/src/dropspider/Errors.java
index 887607791e..a54b8543ab 100644
--- a/src/tools/dropspider/Errors.java
+++ b/tools/SpiderDropFetcher/src/dropspider/Errors.java
@@ -1,4 +1,4 @@
-package tools.dropspider;
+package dropspider;
import java.util.LinkedList;
diff --git a/src/tools/dropspider/Main.java b/tools/SpiderDropFetcher/src/dropspider/Main.java
similarity index 99%
rename from src/tools/dropspider/Main.java
rename to tools/SpiderDropFetcher/src/dropspider/Main.java
index 3b28fd60d3..1d0ae2dfa6 100644
--- a/src/tools/dropspider/Main.java
+++ b/tools/SpiderDropFetcher/src/dropspider/Main.java
@@ -2,7 +2,7 @@
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
-package tools.dropspider;
+package dropspider;
import java.io.BufferedWriter;
import java.io.File;
@@ -37,7 +37,7 @@ public class Main {
private static String[] additionalPagesBB = {"101-120,", "121-140", "141-160", "161-180", "181-200"};
public static void main(String[] args) {
- System.setProperty("wzpath", "wz");
+ System.setProperty("wzpath", "../../wz");
//DataTool.setHardcodedMobNames();
//parsePage("https://bbb.hidden-street.net/monster/nibelung-3");
diff --git a/tools/SpiderDropFetcher/src/provider/MapleCanvas.java b/tools/SpiderDropFetcher/src/provider/MapleCanvas.java
new file mode 100644
index 0000000000..10ab682196
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleCanvas.java
@@ -0,0 +1,30 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+import java.awt.image.BufferedImage;
+
+public interface MapleCanvas {
+ int getHeight();
+ int getWidth();
+ BufferedImage getImage();
+}
diff --git a/tools/SpiderDropFetcher/src/provider/MapleData.java b/tools/SpiderDropFetcher/src/provider/MapleData.java
new file mode 100644
index 0000000000..4d90a93804
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleData.java
@@ -0,0 +1,34 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+import java.util.List;
+import provider.wz.MapleDataType;
+
+public interface MapleData extends MapleDataEntity, Iterable {
+ @Override
+ public String getName();
+ public MapleDataType getType();
+ public List getChildren();
+ public MapleData getChildByPath(String path);
+ public Object getData();
+}
diff --git a/tools/SpiderDropFetcher/src/provider/MapleDataDirectoryEntry.java b/tools/SpiderDropFetcher/src/provider/MapleDataDirectoryEntry.java
new file mode 100644
index 0000000000..cb043e0c94
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleDataDirectoryEntry.java
@@ -0,0 +1,34 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+import java.util.List;
+
+/**
+ *
+ * @author Matze
+ */
+public interface MapleDataDirectoryEntry extends MapleDataEntry {
+ public List getSubdirectories();
+ public List getFiles();
+ public MapleDataEntry getEntry(String name);
+}
diff --git a/tools/SpiderDropFetcher/src/provider/MapleDataEntity.java b/tools/SpiderDropFetcher/src/provider/MapleDataEntity.java
new file mode 100644
index 0000000000..03ff77649c
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleDataEntity.java
@@ -0,0 +1,31 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+/**
+ *
+ * @author Matze
+ */
+public interface MapleDataEntity {
+ public String getName();
+ public MapleDataEntity getParent();
+}
diff --git a/tools/SpiderDropFetcher/src/provider/MapleDataEntry.java b/tools/SpiderDropFetcher/src/provider/MapleDataEntry.java
new file mode 100644
index 0000000000..62db6d0abe
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleDataEntry.java
@@ -0,0 +1,33 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+/**
+ *
+ * @author Matze
+ */
+public interface MapleDataEntry extends MapleDataEntity {
+ public String getName();
+ public int getSize();
+ public int getChecksum();
+ public int getOffset();
+}
diff --git a/tools/SpiderDropFetcher/src/provider/MapleDataFileEntry.java b/tools/SpiderDropFetcher/src/provider/MapleDataFileEntry.java
new file mode 100644
index 0000000000..902130a612
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleDataFileEntry.java
@@ -0,0 +1,30 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+/**
+ *
+ * @author Matze
+ */
+public interface MapleDataFileEntry extends MapleDataEntry {
+ public void setOffset(int offset);
+}
diff --git a/tools/SpiderDropFetcher/src/provider/MapleDataProvider.java b/tools/SpiderDropFetcher/src/provider/MapleDataProvider.java
new file mode 100644
index 0000000000..5237b7ac37
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleDataProvider.java
@@ -0,0 +1,27 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+public interface MapleDataProvider {
+ MapleData getData(String path);
+ MapleDataDirectoryEntry getRoot();
+}
diff --git a/tools/SpiderDropFetcher/src/provider/MapleDataProviderFactory.java b/tools/SpiderDropFetcher/src/provider/MapleDataProviderFactory.java
new file mode 100644
index 0000000000..14753d4406
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleDataProviderFactory.java
@@ -0,0 +1,55 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+import java.io.File;
+import java.io.IOException;
+import provider.wz.WZFile;
+import provider.wz.XMLWZFile;
+
+public class MapleDataProviderFactory {
+ private final static String wzPath = System.getProperty("wzpath");
+
+ private static MapleDataProvider getWZ(File in, boolean provideImages) {
+ if (in.getName().toLowerCase().endsWith("wz") && !in.isDirectory()) {
+ try {
+ return new WZFile(in, provideImages);
+ } catch (IOException e) {
+ throw new RuntimeException("Loading WZ File failed", e);
+ }
+ } else {
+ return new XMLWZFile(in);
+ }
+ }
+
+ public static MapleDataProvider getDataProvider(File in) {
+ return getWZ(in, false);
+ }
+
+ public static MapleDataProvider getImageProvidingDataProvider(File in) {
+ return getWZ(in, true);
+ }
+
+ public static File fileInWZPath(String filename) {
+ return new File(wzPath, filename);
+ }
+}
\ No newline at end of file
diff --git a/tools/SpiderDropFetcher/src/provider/MapleDataTool.java b/tools/SpiderDropFetcher/src/provider/MapleDataTool.java
new file mode 100644
index 0000000000..25f4c7f817
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/MapleDataTool.java
@@ -0,0 +1,145 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider;
+
+import java.awt.Point;
+import java.awt.image.BufferedImage;
+import provider.wz.MapleDataType;
+
+public class MapleDataTool {
+ public static String getString(MapleData data) {
+ return ((String) data.getData());
+ }
+
+ public static String getString(MapleData data, String def) {
+ if (data == null || data.getData() == null) {
+ return def;
+ } else {
+ return ((String) data.getData());
+ }
+ }
+
+ public static String getString(String path, MapleData data) {
+ return getString(data.getChildByPath(path));
+ }
+
+ public static String getString(String path, MapleData data, String def) {
+ return getString(data.getChildByPath(path), def);
+ }
+
+ public static double getDouble(MapleData data) {
+ return ((Double) data.getData()).doubleValue();
+ }
+
+ public static float getFloat(MapleData data) {
+ return ((Float) data.getData()).floatValue();
+ }
+
+ public static int getInt(MapleData data) {
+ if (data == null || data.getData() == null) {
+ return 0;// DEF?
+ }
+ return ((Integer) data.getData()).intValue();
+ }
+
+ public static int getInt(String path, MapleData data) {
+ return getInt(data.getChildByPath(path));
+ }
+
+ public static int getIntConvert(MapleData data) {
+ if (data.getType() == MapleDataType.STRING) {
+ return Integer.parseInt(getString(data));
+ } else {
+ return getInt(data);
+ }
+ }
+
+ public static int getIntConvert(String path, MapleData data) {
+ MapleData d = data.getChildByPath(path);
+ if (d.getType() == MapleDataType.STRING) {
+ return Integer.parseInt(getString(d));
+ } else {
+ return getInt(d);
+ }
+ }
+
+ public static int getInt(MapleData data, int def) {
+ if (data == null || data.getData() == null) {
+ return def;
+ } else if (data.getType() == MapleDataType.STRING) {
+ return Integer.parseInt(getString(data));
+ } else {
+ return ((Integer) data.getData()).intValue();
+ }
+ }
+
+ public static int getInt(String path, MapleData data, int def) {
+ return getInt(data.getChildByPath(path), def);
+ }
+
+ public static int getIntConvert(String path, MapleData data, int def) {
+ MapleData d = data.getChildByPath(path);
+ if (d == null) {
+ return def;
+ }
+ if (d.getType() == MapleDataType.STRING) {
+ try {
+ return Integer.parseInt(getString(d));
+ } catch (NumberFormatException nfe) {
+ nfe.printStackTrace();
+ return def;
+ }
+ } else {
+ return getInt(d, def);
+ }
+ }
+
+ public static BufferedImage getImage(MapleData data) {
+ return ((MapleCanvas) data.getData()).getImage();
+ }
+
+ public static Point getPoint(MapleData data) {
+ return ((Point) data.getData());
+ }
+
+ public static Point getPoint(String path, MapleData data) {
+ return getPoint(data.getChildByPath(path));
+ }
+
+ public static Point getPoint(String path, MapleData data, Point def) {
+ final MapleData pointData = data.getChildByPath(path);
+ if (pointData == null) {
+ return def;
+ }
+ return getPoint(pointData);
+ }
+
+ public static String getFullDataPath(MapleData data) {
+ String path = "";
+ MapleDataEntity myData = data;
+ while (myData != null) {
+ path = myData.getName() + "/" + path;
+ myData = myData.getParent();
+ }
+ return path.substring(0, path.length() - 1);
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/FileStoredPngMapleCanvas.java b/tools/SpiderDropFetcher/src/provider/wz/FileStoredPngMapleCanvas.java
new file mode 100644
index 0000000000..21736c2c16
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/FileStoredPngMapleCanvas.java
@@ -0,0 +1,70 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.awt.image.BufferedImage;
+import java.io.File;
+import java.io.IOException;
+import javax.imageio.ImageIO;
+import provider.MapleCanvas;
+
+public class FileStoredPngMapleCanvas implements MapleCanvas {
+ private File file;
+ private int width;
+ private int height;
+ private BufferedImage image;
+
+ public FileStoredPngMapleCanvas(int width, int height, File fileIn) {
+ this.width = width;
+ this.height = height;
+ this.file = fileIn;
+ }
+
+ @Override
+ public int getHeight() {
+ return height;
+ }
+
+ @Override
+ public int getWidth() {
+ return width;
+ }
+
+ @Override
+ public BufferedImage getImage() {
+ loadImageIfNecessary();
+ return image;
+ }
+
+ private void loadImageIfNecessary() {
+ if (image == null) {
+ try {
+ image = ImageIO.read(file);
+ // replace the dimensions loaded from the wz by the REAL dimensions from the image - should be equal tho
+ width = image.getWidth();
+ height = image.getHeight();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/ImgMapleSound.java b/tools/SpiderDropFetcher/src/provider/wz/ImgMapleSound.java
new file mode 100644
index 0000000000..8add2ccb36
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/ImgMapleSound.java
@@ -0,0 +1,39 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+public class ImgMapleSound {
+ private int dataLength, offset;
+
+ public ImgMapleSound(int dataLength, int offset) {
+ this.dataLength = dataLength;
+ this.offset = offset;
+ }
+
+ public int getDataLength() {
+ return dataLength;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/ListWZFile.java b/tools/SpiderDropFetcher/src/provider/wz/ListWZFile.java
new file mode 100644
index 0000000000..1672a08c59
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/ListWZFile.java
@@ -0,0 +1,86 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.List;
+import provider.MapleDataProviderFactory;
+import tools.data.input.GenericLittleEndianAccessor;
+import tools.data.input.InputStreamByteStream;
+import tools.data.input.LittleEndianAccessor;
+
+public class ListWZFile {
+ private LittleEndianAccessor lea;
+ private List entries = new ArrayList();
+ private static Collection modernImgs = new HashSet();
+
+ public static byte[] xorBytes(byte[] a, byte[] b) {
+ byte[] wusched = new byte[a.length];
+ for (int i = 0; i < a.length; i++) {
+ wusched[i] = (byte) (a[i] ^ b[i]);
+ }
+ return wusched;
+ }
+
+ public ListWZFile(File listwz) throws FileNotFoundException {
+ lea = new GenericLittleEndianAccessor(new InputStreamByteStream(new BufferedInputStream(new FileInputStream(listwz))));
+ while (lea.available() > 0) {
+ int l = lea.readInt() * 2;
+ byte[] chunk = new byte[l];
+ for (int i = 0; i < chunk.length; i++) {
+ chunk[i] = lea.readByte();
+ }
+ lea.readChar();
+ final String value = String.valueOf(WZTool.readListString(chunk));
+ entries.add(value);
+ }
+ entries = Collections.unmodifiableList(entries);
+ }
+
+ public List getEntries() {
+ return entries;
+ }
+
+ public static void init() {
+ final String listWz = System.getProperty("listwz");
+ if (listWz != null) {
+ ListWZFile listwz;
+ try {
+ listwz = new ListWZFile(MapleDataProviderFactory.fileInWZPath("List.wz"));
+ modernImgs = new HashSet(listwz.getEntries());
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ public static boolean isModernImgFile(String path) {
+ return modernImgs.contains(path);
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/MapleDataType.java b/tools/SpiderDropFetcher/src/provider/wz/MapleDataType.java
new file mode 100644
index 0000000000..e074d57d14
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/MapleDataType.java
@@ -0,0 +1,26 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+public enum MapleDataType {
+ NONE, IMG_0x00, SHORT, INT, FLOAT, DOUBLE, STRING, EXTENDED, PROPERTY, CANVAS, VECTOR, CONVEX, SOUND, UOL, UNKNOWN_TYPE, UNKNOWN_EXTENDED_TYPE;
+}
\ No newline at end of file
diff --git a/tools/SpiderDropFetcher/src/provider/wz/PNGMapleCanvas.java b/tools/SpiderDropFetcher/src/provider/wz/PNGMapleCanvas.java
new file mode 100644
index 0000000000..97c2303804
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/PNGMapleCanvas.java
@@ -0,0 +1,151 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.awt.Point;
+import java.awt.image.BufferedImage;
+import java.awt.image.DataBuffer;
+import java.awt.image.DataBufferByte;
+import java.awt.image.PixelInterleavedSampleModel;
+import java.awt.image.Raster;
+import java.awt.image.SampleModel;
+import java.awt.image.WritableRaster;
+import java.util.zip.DataFormatException;
+import java.util.zip.Inflater;
+import provider.MapleCanvas;
+
+public class PNGMapleCanvas implements MapleCanvas {
+ private static final int[] ZAHLEN = new int[]{2, 1, 0, 3};
+ private int height;
+ private int width;
+ private int dataLength;
+ private int format;
+ private byte[] data;
+
+ public PNGMapleCanvas(int width, int height, int dataLength, int format, byte[] data) {
+ super();
+ this.height = height;
+ this.width = width;
+ this.dataLength = dataLength;
+ this.format = format;
+ this.data = data;
+ }
+
+ public int getHeight() {
+ return height;
+ }
+
+ public int getWidth() {
+ return width;
+ }
+
+ public int getFormat() {
+ return format;
+ }
+
+ private byte[] getData() {
+ return data;
+ }
+
+ @Override
+ public BufferedImage getImage() {
+ int sizeUncompressed = 0;
+ int size8888 = 0;
+ int maxWriteBuf = 2;
+ int maxHeight = 3;
+ byte[] writeBuf = new byte[maxWriteBuf];
+ @SuppressWarnings ("unused")
+ byte[] rowPointers = new byte[maxHeight];
+ switch (getFormat()) {
+ case 1:
+ case 513:
+ sizeUncompressed = getHeight() * getWidth() * 4;
+ break;
+ case 2:
+ sizeUncompressed = getHeight() * getWidth() * 8;
+ break;
+ case 517:
+ sizeUncompressed = getHeight() * getWidth() / 128;
+ break;
+ }
+ size8888 = getHeight() * getWidth() * 8;
+ if (size8888 > maxWriteBuf) {
+ maxWriteBuf = size8888;
+ writeBuf = new byte[maxWriteBuf];
+ }
+ if (getHeight() > maxHeight) {
+ maxHeight = getHeight();
+ rowPointers = new byte[maxHeight];
+ }
+ Inflater dec = new Inflater();
+ dec.setInput(getData(), 0, dataLength);
+ int declen = 0;
+ byte[] uc = new byte[sizeUncompressed];
+ try {
+ declen = dec.inflate(uc);
+ } catch (DataFormatException ex) {
+ throw new RuntimeException("zlib fucks", ex);
+ }
+ dec.end();
+ if (getFormat() == 1) {
+ for (int i = 0; i < sizeUncompressed; i++) {
+ byte low = (byte) (uc[i] & 0x0F);
+ byte high = (byte) (uc[i] & 0xF0);
+ writeBuf[(i << 1)] = (byte) (((low << 4) | low) & 0xFF);
+ writeBuf[(i << 1) + 1] = (byte) (high | ((high >>> 4) & 0xF));
+ }
+ } else if (getFormat() == 2) {
+ writeBuf = uc;
+ } else if (getFormat() == 513) {
+ for (int i = 0; i < declen; i += 2) {
+ byte bBits = (byte) ((uc[i] & 0x1F) << 3);
+ byte gBits = (byte) (((uc[i + 1] & 0x07) << 5) | ((uc[i] & 0xE0) >> 3));
+ byte rBits = (byte) (uc[i + 1] & 0xF8);
+ writeBuf[(i << 1)] = (byte) (bBits | (bBits >> 5));
+ writeBuf[(i << 1) + 1] = (byte) (gBits | (gBits >> 6));
+ writeBuf[(i << 1) + 2] = (byte) (rBits | (rBits >> 5));
+ writeBuf[(i << 1) + 3] = (byte) 0xFF;
+ }
+ } else if (getFormat() == 517) {
+ byte b = 0x00;
+ int pixelIndex = 0;
+ for (int i = 0; i < declen; i++) {
+ for (int j = 0; j < 8; j++) {
+ b = (byte) (((uc[i] & (0x01 << (7 - j))) >> (7 - j)) * 255);
+ for (int k = 0; k < 16; k++) {
+ pixelIndex = (i << 9) + (j << 6) + k * 2;
+ writeBuf[pixelIndex] = b;
+ writeBuf[pixelIndex + 1] = b;
+ writeBuf[pixelIndex + 2] = b;
+ writeBuf[pixelIndex + 3] = (byte) 0xFF;
+ }
+ }
+ }
+ }
+ DataBufferByte imgData = new DataBufferByte(writeBuf, sizeUncompressed);
+ SampleModel sm = new PixelInterleavedSampleModel(DataBuffer.TYPE_BYTE, getWidth(), getHeight(), 4, getWidth() * 4, ZAHLEN);
+ WritableRaster imgRaster = Raster.createWritableRaster(sm, imgData, new Point(0, 0));
+ BufferedImage aa = new BufferedImage(getWidth(), getHeight(), BufferedImage.TYPE_INT_ARGB);
+ aa.setData(imgRaster);
+ return aa;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/WZDirectoryEntry.java b/tools/SpiderDropFetcher/src/provider/wz/WZDirectoryEntry.java
new file mode 100644
index 0000000000..d24b8cb2b9
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/WZDirectoryEntry.java
@@ -0,0 +1,68 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import provider.MapleDataDirectoryEntry;
+import provider.MapleDataEntity;
+import provider.MapleDataEntry;
+import provider.MapleDataFileEntry;
+
+public class WZDirectoryEntry extends WZEntry implements MapleDataDirectoryEntry {
+ private List subdirs = new ArrayList();
+ private List files = new ArrayList();
+ private Map entries = new HashMap();
+
+ public WZDirectoryEntry(String name, int size, int checksum, MapleDataEntity parent) {
+ super(name, size, checksum, parent);
+ }
+
+ public WZDirectoryEntry() {
+ super(null, 0, 0, null);
+ }
+
+ public void addDirectory(MapleDataDirectoryEntry dir) {
+ subdirs.add(dir);
+ entries.put(dir.getName(), dir);
+ }
+
+ public void addFile(MapleDataFileEntry fileEntry) {
+ files.add(fileEntry);
+ entries.put(fileEntry.getName(), fileEntry);
+ }
+
+ public List getSubdirectories() {
+ return Collections.unmodifiableList(subdirs);
+ }
+
+ public List getFiles() {
+ return Collections.unmodifiableList(files);
+ }
+
+ public MapleDataEntry getEntry(String name) {
+ return entries.get(name);
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/WZEntry.java b/tools/SpiderDropFetcher/src/provider/wz/WZEntry.java
new file mode 100644
index 0000000000..1e921b2082
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/WZEntry.java
@@ -0,0 +1,61 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import provider.MapleDataEntity;
+import provider.MapleDataEntry;
+
+public class WZEntry implements MapleDataEntry {
+ private String name;
+ private int size;
+ private int checksum;
+ private int offset;
+ private MapleDataEntity parent;
+
+ public WZEntry(String name, int size, int checksum, MapleDataEntity parent) {
+ super();
+ this.name = name;
+ this.size = size;
+ this.checksum = checksum;
+ this.parent = parent;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public int getSize() {
+ return size;
+ }
+
+ public int getChecksum() {
+ return checksum;
+ }
+
+ public int getOffset() {
+ return offset;
+ }
+
+ public MapleDataEntity getParent() {
+ return parent;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/WZFile.java b/tools/SpiderDropFetcher/src/provider/wz/WZFile.java
new file mode 100644
index 0000000000..c6c0abf537
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/WZFile.java
@@ -0,0 +1,154 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+import provider.MapleData;
+import provider.MapleDataDirectoryEntry;
+import provider.MapleDataFileEntry;
+import provider.MapleDataProvider;
+import tools.data.input.GenericLittleEndianAccessor;
+import tools.data.input.GenericSeekableLittleEndianAccessor;
+import tools.data.input.InputStreamByteStream;
+import tools.data.input.LittleEndianAccessor;
+import tools.data.input.RandomAccessByteStream;
+import tools.data.input.SeekableLittleEndianAccessor;
+
+public class WZFile implements MapleDataProvider {
+ static {
+ ListWZFile.init();
+ }
+ private File wzfile;
+ private LittleEndianAccessor lea;
+ private SeekableLittleEndianAccessor slea;
+ private int headerSize;
+ private WZDirectoryEntry root;
+ private boolean provideImages;
+ private int cOffset;
+
+ public WZFile(File wzfile, boolean provideImages) throws IOException {
+ this.wzfile = wzfile;
+ lea = new GenericLittleEndianAccessor(new InputStreamByteStream(new BufferedInputStream(new FileInputStream(wzfile))));
+ RandomAccessFile raf = new RandomAccessFile(wzfile, "r");
+ slea = new GenericSeekableLittleEndianAccessor(new RandomAccessByteStream(raf));
+ root = new WZDirectoryEntry(wzfile.getName(), 0, 0, null);
+ this.provideImages = provideImages;
+ load();
+ }
+
+ private void load() throws IOException {
+ lea.readAsciiString(4);
+ lea.readInt();
+ lea.readInt();
+ headerSize = lea.readInt();
+ lea.readNullTerminatedAsciiString();
+ lea.readShort();
+ parseDirectory(root);
+ cOffset = (int) lea.getBytesRead();
+ getOffsets(root);
+ }
+
+ private void getOffsets(MapleDataDirectoryEntry dir) {
+ for (MapleDataFileEntry file : dir.getFiles()) {
+ file.setOffset(cOffset);
+ cOffset += file.getSize();
+ }
+ for (MapleDataDirectoryEntry sdir : dir.getSubdirectories()) {
+ getOffsets(sdir);
+ }
+ }
+
+ private void parseDirectory(WZDirectoryEntry dir) {
+ int entries = WZTool.readValue(lea);
+ for (int i = 0; i < entries; i++) {
+ byte marker = lea.readByte();
+ String name = null;
+ int size, checksum;
+ switch (marker) {
+ case 0x02:
+ name = WZTool.readDecodedStringAtOffsetAndReset(slea, lea.readInt() + this.headerSize + 1);
+ size = WZTool.readValue(lea);
+ checksum = WZTool.readValue(lea);
+ lea.readInt(); //dummy int
+ dir.addFile(new WZFileEntry(name, size, checksum, dir));
+ break;
+ case 0x03:
+ case 0x04:
+ name = WZTool.readDecodedString(lea);
+ size = WZTool.readValue(lea);
+ checksum = WZTool.readValue(lea);
+ lea.readInt(); //dummy int
+ if (marker == 3) {
+ dir.addDirectory(new WZDirectoryEntry(name, size, checksum, dir));
+ } else {
+ dir.addFile(new WZFileEntry(name, size, checksum, dir));
+ }
+ break;
+ default:
+ }
+ }
+ for (MapleDataDirectoryEntry idir : dir.getSubdirectories()) {
+ parseDirectory((WZDirectoryEntry) idir);
+ }
+ }
+
+ public WZIMGFile getImgFile(String path) throws IOException {
+ String segments[] = path.split("/");
+ WZDirectoryEntry dir = root;
+ for (int x = 0; x < segments.length - 1; x++) {
+ dir = (WZDirectoryEntry) dir.getEntry(segments[x]);
+ if (dir == null) {
+ return null;
+ }
+ }
+ WZFileEntry entry = (WZFileEntry) dir.getEntry(segments[segments.length - 1]);
+ if (entry == null) {
+ return null;
+ }
+ String fullPath = wzfile.getName().substring(0, wzfile.getName().length() - 3).toLowerCase() + "/" + path;
+ return new WZIMGFile(this.wzfile, entry, provideImages, ListWZFile.isModernImgFile(fullPath));
+ }
+
+ @Override
+ public synchronized MapleData getData(String path) {
+ try {
+ WZIMGFile imgFile = getImgFile(path);
+ if (imgFile == null) {
+ return null;
+ }
+ MapleData ret = imgFile.getRoot();
+ return ret;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ return null;
+ }
+
+ @Override
+ public MapleDataDirectoryEntry getRoot() {
+ return root;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/WZFileEntry.java b/tools/SpiderDropFetcher/src/provider/wz/WZFileEntry.java
new file mode 100644
index 0000000000..792371d9cf
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/WZFileEntry.java
@@ -0,0 +1,42 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import provider.MapleDataEntity;
+import provider.MapleDataFileEntry;
+
+public class WZFileEntry extends WZEntry implements MapleDataFileEntry {
+ private int offset;
+
+ public WZFileEntry(String name, int size, int checksum, MapleDataEntity parent) {
+ super(name, size, checksum, parent);
+ }
+
+ @Override
+ public int getOffset() {
+ return offset;
+ }
+
+ public void setOffset(int offset) {
+ this.offset = offset;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/WZIMGEntry.java b/tools/SpiderDropFetcher/src/provider/wz/WZIMGEntry.java
new file mode 100644
index 0000000000..385d785183
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/WZIMGEntry.java
@@ -0,0 +1,118 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import provider.MapleData;
+import provider.MapleDataEntity;
+
+public class WZIMGEntry implements MapleData {
+ private String name;
+ private MapleDataType type;
+ private List children = new ArrayList(10);
+ private Object data;
+ private MapleDataEntity parent;
+
+ public WZIMGEntry(MapleDataEntity parent) {
+ this.parent = parent;
+ }
+
+ @Override
+ public String getName() {
+ return name;
+ }
+
+ @Override
+ public MapleDataType getType() {
+ return type;
+ }
+
+ @Override
+ public List getChildren() {
+ return Collections.unmodifiableList(children);
+ }
+
+ @Override
+ public MapleData getChildByPath(String path) {
+ String segments[] = path.split("/");
+ if (segments[0].equals("..")) {
+ return ((MapleData) getParent()).getChildByPath(path.substring(path.indexOf("/") + 1));
+ }
+ MapleData ret = this;
+ for (int x = 0; x < segments.length; x++) {
+ boolean foundChild = false;
+ for (MapleData child : ret.getChildren()) {
+ if (child.getName().equals(segments[x])) {
+ ret = child;
+ foundChild = true;
+ break;
+ }
+ }
+ if (!foundChild) {
+ return null;
+ }
+ }
+ return ret;
+ }
+
+ @Override
+ public Object getData() {
+ return data;
+ }
+
+ public void setName(String name) {
+ this.name = name;
+ }
+
+ public void setType(MapleDataType type) {
+ this.type = type;
+ }
+
+ public void setData(Object data) {
+ this.data = data;
+ }
+
+ public void addChild(WZIMGEntry entry) {
+ children.add(entry);
+ }
+
+ @Override
+ public Iterator iterator() {
+ return getChildren().iterator();
+ }
+
+ @Override
+ public String toString() {
+ return getName() + ":" + getData();
+ }
+
+ public MapleDataEntity getParent() {
+ return parent;
+ }
+
+ public void finish() {
+ ((ArrayList) children).trimToSize();
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/WZIMGFile.java b/tools/SpiderDropFetcher/src/provider/wz/WZIMGFile.java
new file mode 100644
index 0000000000..bec06c78bd
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/WZIMGFile.java
@@ -0,0 +1,227 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.awt.Point;
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.RandomAccessFile;
+import tools.data.input.GenericSeekableLittleEndianAccessor;
+import tools.data.input.RandomAccessByteStream;
+import tools.data.input.SeekableLittleEndianAccessor;
+
+public class WZIMGFile {
+ private WZFileEntry file;
+ private WZIMGEntry root;
+ private boolean provideImages;
+ @SuppressWarnings ("unused")
+ private boolean modernImg;
+
+ public WZIMGFile(File wzfile, WZFileEntry file, boolean provideImages, boolean modernImg) throws IOException {
+ RandomAccessFile raf = new RandomAccessFile(wzfile, "r");
+ SeekableLittleEndianAccessor slea = new GenericSeekableLittleEndianAccessor(new RandomAccessByteStream(raf));
+ slea.seek(file.getOffset());
+ this.file = file;
+ this.provideImages = provideImages;
+ root = new WZIMGEntry(file.getParent());
+ root.setName(file.getName());
+ root.setType(MapleDataType.EXTENDED);
+ this.modernImg = modernImg;
+ parseExtended(root, slea, 0);
+ root.finish();
+ raf.close();
+ }
+
+ protected void dumpImg(OutputStream out, SeekableLittleEndianAccessor slea) throws IOException {
+ DataOutputStream os = new DataOutputStream(out);
+ long oldPos = slea.getPosition();
+ slea.seek(file.getOffset());
+ for (int x = 0; x < file.getSize(); x++) {
+ os.write(slea.readByte());
+ }
+ slea.seek(oldPos);
+ }
+
+ public WZIMGEntry getRoot() {
+ return root;
+ }
+
+ private void parse(WZIMGEntry entry, SeekableLittleEndianAccessor slea) {
+ byte marker = slea.readByte();
+ switch (marker) {
+ case 0: {
+ String name = WZTool.readDecodedString(slea);
+ entry.setName(name);
+ break;
+ }
+ case 1: {
+ String name = WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt());
+ entry.setName(name);
+ break;
+ }
+ default:
+ System.out.println("Unknown Image identifier: " + marker + " at offset " + (slea.getPosition() - file.getOffset()));
+ }
+ marker = slea.readByte();
+ switch (marker) {
+ case 0:
+ entry.setType(MapleDataType.IMG_0x00);
+ break;
+ case 2:
+ case 11: //??? no idea, since 0.49
+ entry.setType(MapleDataType.SHORT);
+ entry.setData(Short.valueOf(slea.readShort()));
+ break;
+ case 3:
+ entry.setType(MapleDataType.INT);
+ entry.setData(Integer.valueOf(WZTool.readValue(slea)));
+ break;
+ case 4:
+ entry.setType(MapleDataType.FLOAT);
+ entry.setData(Float.valueOf(WZTool.readFloatValue(slea)));
+ break;
+ case 5:
+ entry.setType(MapleDataType.DOUBLE);
+ entry.setData(Double.valueOf(slea.readDouble()));
+ break;
+ case 8:
+ entry.setType(MapleDataType.STRING);
+ byte iMarker = slea.readByte();
+ if (iMarker == 0) {
+ entry.setData(WZTool.readDecodedString(slea));
+ } else if (iMarker == 1) {
+ entry.setData(WZTool.readDecodedStringAtOffsetAndReset(slea, slea.readInt() + file.getOffset()));
+ } else {
+ System.out.println("Unknown String type " + iMarker);
+ }
+ break;
+ case 9:
+ entry.setType(MapleDataType.EXTENDED);
+ long endOfExtendedBlock = slea.readInt();
+ endOfExtendedBlock += slea.getPosition();
+ parseExtended(entry, slea, endOfExtendedBlock);
+ break;
+ default:
+ System.out.println("Unknown Image type " + marker);
+ }
+ }
+
+ private void parseExtended(WZIMGEntry entry, SeekableLittleEndianAccessor slea, long endOfExtendedBlock) {
+ byte marker = slea.readByte();
+ String type;
+ switch (marker) {
+ case 0x73:
+ type = WZTool.readDecodedString(slea);
+ break;
+ case 0x1B:
+ type = WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt());
+ break;
+ default:
+ throw new RuntimeException("Unknown extended image identifier: " + marker + " at offset " +
+ (slea.getPosition() - file.getOffset()));
+ }
+ if (type.equals("Property")) {
+ entry.setType(MapleDataType.PROPERTY);
+ slea.readByte();
+ slea.readByte();
+ int children = WZTool.readValue(slea);
+ for (int i = 0; i < children; i++) {
+ WZIMGEntry cEntry = new WZIMGEntry(entry);
+ parse(cEntry, slea);
+ cEntry.finish();
+ entry.addChild(cEntry);
+ }
+ } else if (type.equals("Canvas")) {
+ entry.setType(MapleDataType.CANVAS);
+ slea.readByte();
+ marker = slea.readByte();
+ if (marker == 0) {
+ // do nothing
+ } else if (marker == 1) {
+ slea.readByte();
+ slea.readByte();
+ int children = WZTool.readValue(slea);
+ for (int i = 0; i < children; i++) {
+ WZIMGEntry child = new WZIMGEntry(entry);
+ parse(child, slea);
+ child.finish();
+ entry.addChild(child);
+ }
+ } else {
+ System.out.println("Canvas marker != 1 (" + marker + ")");
+ }
+ int width = WZTool.readValue(slea);
+ int height = WZTool.readValue(slea);
+ int format = WZTool.readValue(slea);
+ int format2 = slea.readByte();
+ slea.readInt();
+ int dataLength = slea.readInt() - 1;
+ slea.readByte();
+ if (provideImages) {
+ byte[] pngdata = slea.read(dataLength);
+ entry.setData(new PNGMapleCanvas(width, height, dataLength, format + format2, pngdata));
+ } else {
+ entry.setData(new PNGMapleCanvas(width, height, dataLength, format + format2, null));
+ slea.skip(dataLength);
+ }
+ } else if (type.equals("Shape2D#Vector2D")) {
+ entry.setType(MapleDataType.VECTOR);
+ int x = WZTool.readValue(slea);
+ int y = WZTool.readValue(slea);
+ entry.setData(new Point(x, y));
+ } else if (type.equals("Shape2D#Convex2D")) {
+ int children = WZTool.readValue(slea);
+ for (int i = 0; i < children; i++) {
+ WZIMGEntry cEntry = new WZIMGEntry(entry);
+ parseExtended(cEntry, slea, 0);
+ cEntry.finish();
+ entry.addChild(cEntry);
+ }
+ } else if (type.equals("Sound_DX8")) {
+ entry.setType(MapleDataType.SOUND);
+ slea.readByte();
+ int dataLength = WZTool.readValue(slea);
+ WZTool.readValue(slea); // no clue what this is
+ int offset = (int) slea.getPosition();
+ entry.setData(new ImgMapleSound(dataLength, offset - file.getOffset()));
+ slea.seek(endOfExtendedBlock);
+ } else if (type.equals("UOL")) {
+ entry.setType(MapleDataType.UOL);
+ slea.readByte();
+ byte uolmarker = slea.readByte();
+ switch (uolmarker) {
+ case 0:
+ entry.setData(WZTool.readDecodedString(slea));
+ break;
+ case 1:
+ entry.setData(WZTool.readDecodedStringAtOffsetAndReset(slea, file.getOffset() + slea.readInt()));
+ break;
+ default:
+ System.out.println("Unknown UOL marker: " + uolmarker + " " + entry.getName());
+ }
+ } else {
+ throw new RuntimeException("Unhandled extended type: " + type);
+ }
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/WZTool.java b/tools/SpiderDropFetcher/src/provider/wz/WZTool.java
new file mode 100644
index 0000000000..85e1c8d90b
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/WZTool.java
@@ -0,0 +1,187 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.security.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.spec.SecretKeySpec;
+import tools.data.input.LittleEndianAccessor;
+import tools.data.input.SeekableLittleEndianAccessor;
+
+/*
+ * Ported Code, see WZFile.java for more info
+ */
+public class WZTool {
+ private static byte[] encKey;
+
+ static {
+ byte[] iv = new byte[]{(byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,
+ (byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,
+ (byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,
+ (byte) 0x4d, (byte) 0x23, (byte) 0xc7, (byte) 0x2b,};
+ byte[] key = new byte[]{(byte) 0x13, 0x00, 0x00, 0x00,
+ (byte) 0x08, 0x00, 0x00, 0x00,
+ (byte) 0x06, 0x00, 0x00, 0x00,
+ (byte) 0xB4, 0x00, 0x00, 0x00,
+ (byte) 0x1B, 0x00, 0x00, 0x00,
+ (byte) 0x0F, 0x00, 0x00, 0x00,
+ (byte) 0x33, 0x00, 0x00, 0x00,
+ (byte) 0x52, 0x00, 0x00, 0x00
+ };
+ Cipher cipher = null;
+ SecretKeySpec skeySpec = new SecretKeySpec(key, "AES");
+ try {
+ cipher = Cipher.getInstance("AES");
+ } catch (NoSuchAlgorithmException e) {
+ e.printStackTrace();
+ } catch (NoSuchPaddingException e) {
+ e.printStackTrace();
+ }
+ try {
+ cipher.init(Cipher.ENCRYPT_MODE, skeySpec);
+ } catch (InvalidKeyException e) {
+ e.printStackTrace();
+ }
+ encKey = new byte[0xFFFF];
+ for (int i = 0; i < (0xFFFF / 16); i++) {
+ try {
+ iv = cipher.doFinal(iv);
+ } catch (IllegalBlockSizeException e) {
+ e.printStackTrace();
+ } catch (BadPaddingException e) {
+ e.printStackTrace();
+ }
+ System.arraycopy(iv, 0, encKey, (i * 16), 16);
+ }
+ try {
+ iv = cipher.doFinal(iv);
+ } catch (IllegalBlockSizeException e) {
+ e.printStackTrace();
+ } catch (BadPaddingException e) {
+ e.printStackTrace();
+ }
+ System.arraycopy(iv, 0, encKey, 65520, 15);
+ }
+
+ public static byte[] readListString(byte[] str) {
+ for (int i = 0; i < str.length; i++) {
+ str[i] = (byte) (str[i] ^ encKey[i]);
+ }
+ return str;
+ }
+
+ public static String readDecodedString(LittleEndianAccessor llea) {
+ int strLength;
+ byte b = llea.readByte();
+ if (b == 0x00) {
+ return "";
+ }
+ if (b >= 0) {
+ if (b == 0x7F) {
+ strLength = llea.readInt();
+ } else {
+ strLength = (int) b;
+ }
+ if (strLength < 0) {
+ return "";
+ }
+ byte str[] = new byte[strLength * 2];
+ for (int i = 0; i < strLength * 2; i++) {
+ str[i] = llea.readByte();
+ }
+ return DecryptUnicodeStr(str);
+ } else {
+ if (b == -128) {
+ strLength = llea.readInt();
+ } else {
+ strLength = -b;
+ }
+ if (strLength < 0) {
+ return "";
+ }
+ byte str[] = new byte[strLength];
+ for (int i = 0; i < strLength; i++) {
+ str[i] = llea.readByte();
+ }
+ return DecryptAsciiStr(str);
+ }
+ }
+
+ public static String DecryptAsciiStr(byte[] str) {
+ byte xorByte = (byte) 0xAA;
+ for (int i = 0; i < str.length; i++) {
+ str[i] = (byte) (str[i] ^ xorByte ^ encKey[i]);
+ xorByte++;
+ }
+ return new String(str);
+ }
+
+ public static String DecryptUnicodeStr(byte[] str) {
+ int xorByte = 0xAAAA;
+ char[] charRet = new char[str.length / 2];
+ for (int i = 0; i < str.length; i++) {
+ str[i] = (byte) (str[i] ^ encKey[i]);
+ }
+ for (int i = 0; i < (str.length / 2); i++) {
+ char toXor = (char) ((str[i] << 8) | str[i + 1]);
+ charRet[i] = (char) (toXor ^ xorByte);
+ xorByte++;
+ }
+ return String.valueOf(charRet);
+ }
+
+ public static String readDecodedStringAtOffset(SeekableLittleEndianAccessor slea, int offset) {
+ slea.seek(offset);
+ return readDecodedString(slea);
+ }
+
+ public static String readDecodedStringAtOffsetAndReset(SeekableLittleEndianAccessor slea, int offset) {
+ long pos = 0;
+ pos = slea.getPosition();
+ slea.seek(offset);
+ String ret = readDecodedString(slea);
+ slea.seek(pos);
+ return ret;
+ }
+
+ public static int readValue(LittleEndianAccessor lea) {
+ byte b = lea.readByte();
+ if (b == -128) {
+ return lea.readInt();
+ } else {
+ return ((int) b);
+ }
+ }
+
+ public static float readFloatValue(LittleEndianAccessor lea) {
+ byte b = lea.readByte();
+ if (b == -128) {
+ return lea.readFloat();
+ } else {
+ return 0;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tools/SpiderDropFetcher/src/provider/wz/XMLDomMapleData.java b/tools/SpiderDropFetcher/src/provider/wz/XMLDomMapleData.java
new file mode 100644
index 0000000000..f8756a8696
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/XMLDomMapleData.java
@@ -0,0 +1,225 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package provider.wz;
+
+import java.awt.Point;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.text.NumberFormat;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.ParserConfigurationException;
+import provider.MapleData;
+import provider.MapleDataEntity;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.SAXException;
+
+public class XMLDomMapleData implements MapleData {
+ private Node node;
+ private File imageDataDir;
+ private final static NumberFormat nfParser = NumberFormat.getInstance(Locale.FRANCE);
+
+ public XMLDomMapleData(FileInputStream fis, File imageDataDir) {
+ try {
+ DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
+ DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
+ Document document = documentBuilder.parse(fis);
+ this.node = document.getFirstChild();
+ } catch (ParserConfigurationException e) {
+ throw new RuntimeException(e);
+ } catch (SAXException e) {
+ throw new RuntimeException(e);
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ this.imageDataDir = imageDataDir;
+ }
+
+ private XMLDomMapleData(Node node) {
+ this.node = node;
+ }
+
+ @Override
+ public synchronized MapleData getChildByPath(String path) { // the whole XML reading system seems susceptible to give nulls on strenuous read scenarios
+ String segments[] = path.split("/");
+ if (segments[0].equals("..")) {
+ return ((MapleData) getParent()).getChildByPath(path.substring(path.indexOf("/") + 1));
+ }
+
+ Node myNode;
+ myNode = node;
+ for (String s : segments) {
+ NodeList childNodes = myNode.getChildNodes();
+ boolean foundChild = false;
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node childNode = childNodes.item(i);
+ if (childNode.getNodeType() == Node.ELEMENT_NODE && childNode.getAttributes().getNamedItem("name").getNodeValue().equals(s)) {
+ myNode = childNode;
+ foundChild = true;
+ break;
+ }
+ }
+ if (!foundChild) {
+ return null;
+ }
+ }
+
+ XMLDomMapleData ret = new XMLDomMapleData(myNode);
+ ret.imageDataDir = new File(imageDataDir, getName() + "/" + path).getParentFile();
+ return ret;
+ }
+
+ @Override
+ public synchronized List getChildren() {
+ List ret = new ArrayList<>();
+
+ NodeList childNodes = node.getChildNodes();
+ for (int i = 0; i < childNodes.getLength(); i++) {
+ Node childNode = childNodes.item(i);
+ if (childNode.getNodeType() == Node.ELEMENT_NODE) {
+ XMLDomMapleData child = new XMLDomMapleData(childNode);
+ child.imageDataDir = new File(imageDataDir, getName());
+ ret.add(child);
+ }
+ }
+
+ return ret;
+ }
+
+ public synchronized static Number parseNumber(String value) {
+ try {
+ return nfParser.parse(value);
+ } catch(Exception e) {
+ e.printStackTrace();
+ return 0.0f;
+ }
+ }
+
+ @Override
+ public synchronized Object getData() {
+ NamedNodeMap attributes = node.getAttributes();
+ MapleDataType type = getType();
+ switch (type) {
+ case DOUBLE:
+ case FLOAT:
+ case INT:
+ case SHORT: {
+ String value = attributes.getNamedItem("value").getNodeValue();
+ Number nval = parseNumber(value);
+
+ switch (type) {
+ case DOUBLE:
+ return nval.doubleValue();
+ case FLOAT:
+ return nval.floatValue();
+ case INT:
+ return nval.intValue();
+ case SHORT:
+ return nval.shortValue();
+ default:
+ return null;
+ }
+ }
+ case STRING:
+ case UOL: {
+ String value = attributes.getNamedItem("value").getNodeValue();
+ return value;
+ }
+ case VECTOR: {
+ String x = attributes.getNamedItem("x").getNodeValue();
+ String y = attributes.getNamedItem("y").getNodeValue();
+ return new Point(Integer.parseInt(x), Integer.parseInt(y));
+ }
+ case CANVAS: {
+ String width = attributes.getNamedItem("width").getNodeValue();
+ String height = attributes.getNamedItem("height").getNodeValue();
+ return new FileStoredPngMapleCanvas(Integer.parseInt(width), Integer.parseInt(height), new File(
+ imageDataDir, getName() + ".png"));
+ }
+ default:
+ return null;
+ }
+ }
+
+ @Override
+ public synchronized MapleDataType getType() {
+ String nodeName = node.getNodeName();
+
+ switch (nodeName) {
+ case "imgdir":
+ return MapleDataType.PROPERTY;
+ case "canvas":
+ return MapleDataType.CANVAS;
+ case "convex":
+ return MapleDataType.CONVEX;
+ case "sound":
+ return MapleDataType.SOUND;
+ case "uol":
+ return MapleDataType.UOL;
+ case "double":
+ return MapleDataType.DOUBLE;
+ case "float":
+ return MapleDataType.FLOAT;
+ case "int":
+ return MapleDataType.INT;
+ case "short":
+ return MapleDataType.SHORT;
+ case "string":
+ return MapleDataType.STRING;
+ case "vector":
+ return MapleDataType.VECTOR;
+ case "null":
+ return MapleDataType.IMG_0x00;
+ }
+ return null;
+ }
+
+ @Override
+ public synchronized MapleDataEntity getParent() {
+ Node parentNode;
+ parentNode = node.getParentNode();
+ if (parentNode.getNodeType() == Node.DOCUMENT_NODE) {
+ return null;
+ }
+ XMLDomMapleData parentData = new XMLDomMapleData(parentNode);
+ parentData.imageDataDir = imageDataDir.getParentFile();
+ return parentData;
+ }
+
+ @Override
+ public synchronized String getName() {
+ return node.getAttributes().getNamedItem("name").getNodeValue();
+ }
+
+ @Override
+ public synchronized Iterator iterator() {
+ return getChildren().iterator();
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/provider/wz/XMLWZFile.java b/tools/SpiderDropFetcher/src/provider/wz/XMLWZFile.java
new file mode 100644
index 0000000000..2a7694fdc9
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/provider/wz/XMLWZFile.java
@@ -0,0 +1,85 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package provider.wz;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import provider.MapleData;
+import provider.MapleDataDirectoryEntry;
+import provider.MapleDataProvider;
+
+public class XMLWZFile implements MapleDataProvider {
+ private File root;
+ private WZDirectoryEntry rootForNavigation;
+
+ public XMLWZFile(File fileIn) {
+ root = fileIn;
+ rootForNavigation = new WZDirectoryEntry(fileIn.getName(), 0, 0, null);
+ fillMapleDataEntitys(root, rootForNavigation);
+ }
+
+ private void fillMapleDataEntitys(File lroot, WZDirectoryEntry wzdir) {
+ for (File file : lroot.listFiles()) {
+ String fileName = file.getName();
+ if (file.isDirectory() && !fileName.endsWith(".img")) {
+ WZDirectoryEntry newDir = new WZDirectoryEntry(fileName, 0, 0, wzdir);
+ wzdir.addDirectory(newDir);
+ fillMapleDataEntitys(file, newDir);
+ } else if (fileName.endsWith(".xml")) {
+ wzdir.addFile(new WZFileEntry(fileName.substring(0, fileName.length() - 4), 0, 0, wzdir));
+ }
+ }
+ }
+
+ @Override
+ public MapleData getData(String path) {
+ File dataFile = new File(root, path + ".xml");
+ File imageDataDir = new File(root, path);
+ if (!dataFile.exists()) {
+ return null;//bitches
+ }
+ FileInputStream fis;
+ try {
+ fis = new FileInputStream(dataFile);
+ } catch (FileNotFoundException e) {
+ throw new RuntimeException("Datafile " + path + " does not exist in " + root.getAbsolutePath());
+ }
+ final XMLDomMapleData domMapleData;
+ try {
+ domMapleData = new XMLDomMapleData(fis, imageDataDir.getParentFile());
+ } finally {
+ try {
+ fis.close();
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return domMapleData;
+ }
+
+ @Override
+ public MapleDataDirectoryEntry getRoot() {
+ return rootForNavigation;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/server/MapleItemInformationProvider.java b/tools/SpiderDropFetcher/src/server/MapleItemInformationProvider.java
new file mode 100644
index 0000000000..b215841610
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/server/MapleItemInformationProvider.java
@@ -0,0 +1,147 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+ */
+package server;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import provider.MapleData;
+import provider.MapleDataProvider;
+import provider.MapleDataProviderFactory;
+import provider.MapleDataTool;
+import tools.Pair;
+
+/**
+ *
+ * @author Matze
+ *
+ */
+public class MapleItemInformationProvider {
+ private final static MapleItemInformationProvider instance = new MapleItemInformationProvider();
+
+ public static MapleItemInformationProvider getInstance() {
+ return instance;
+ }
+
+ protected MapleDataProvider itemData;
+ protected MapleDataProvider equipData;
+ protected MapleDataProvider stringData;
+ protected MapleDataProvider etcData;
+ protected MapleData cashStringData;
+ protected MapleData consumeStringData;
+ protected MapleData eqpStringData;
+ protected MapleData etcStringData;
+ protected MapleData insStringData;
+ protected MapleData petStringData;
+ protected Map isQuestItemCache = new HashMap<>();
+ protected Map isPartyQuestItemCache = new HashMap<>();
+ protected List> itemNameCache = new ArrayList<>();
+
+ private MapleItemInformationProvider() {
+ itemData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Item.wz"));
+ equipData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Character.wz"));
+ stringData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz"));
+ etcData = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Etc.wz"));
+ cashStringData = stringData.getData("Cash.img");
+ consumeStringData = stringData.getData("Consume.img");
+ eqpStringData = stringData.getData("Eqp.img");
+ etcStringData = stringData.getData("Etc.img");
+ insStringData = stringData.getData("Ins.img");
+ petStringData = stringData.getData("Pet.img");
+
+ isQuestItemCache.put(0, false);
+ isPartyQuestItemCache.put(0, false);
+ }
+
+// public MapleInventoryType getInventoryType(int itemId) {
+// if (inventoryTypeCache.containsKey(itemId)) {
+// return inventoryTypeCache.get(itemId);
+// }
+// MapleInventoryType ret;
+// String idStr = "0" + String.valueOf(itemId);
+// MapleDataDirectoryEntry root = itemData.getRoot();
+// for (MapleDataDirectoryEntry topDir : root.getSubdirectories()) {
+// for (MapleDataFileEntry iFile : topDir.getFiles()) {
+// if (iFile.getName().equals(idStr.substring(0, 4) + ".img")) {
+// ret = MapleInventoryType.getByWZName(topDir.getName());
+// inventoryTypeCache.put(itemId, ret);
+// return ret;
+// } else if (iFile.getName().equals(idStr.substring(1) + ".img")) {
+// ret = MapleInventoryType.getByWZName(topDir.getName());
+// inventoryTypeCache.put(itemId, ret);
+// return ret;
+// }
+// }
+// }
+// root = equipData.getRoot();
+// for (MapleDataDirectoryEntry topDir : root.getSubdirectories()) {
+// for (MapleDataFileEntry iFile : topDir.getFiles()) {
+// if (iFile.getName().equals(idStr + ".img")) {
+// ret = MapleInventoryType.EQUIP;
+// inventoryTypeCache.put(itemId, ret);
+// return ret;
+// }
+// }
+// }
+// ret = MapleInventoryType.UNDEFINED;
+// inventoryTypeCache.put(itemId, ret);
+// return ret;
+// }
+
+ public List> getAllItems() {
+ if (!itemNameCache.isEmpty()) {
+ return itemNameCache;
+ }
+ List> itemPairs = new ArrayList<>();
+ MapleData itemsData;
+ itemsData = stringData.getData("Cash.img");
+ for (MapleData itemFolder : itemsData.getChildren()) {
+ itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME")));
+ }
+ itemsData = stringData.getData("Consume.img");
+ for (MapleData itemFolder : itemsData.getChildren()) {
+ itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME")));
+ }
+ itemsData = stringData.getData("Eqp.img").getChildByPath("Eqp");
+ for (MapleData eqpType : itemsData.getChildren()) {
+ for (MapleData itemFolder : eqpType.getChildren()) {
+ itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME")));
+ }
+ }
+ itemsData = stringData.getData("Etc.img").getChildByPath("Etc");
+ for (MapleData itemFolder : itemsData.getChildren()) {
+ itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME")));
+ }
+ itemsData = stringData.getData("Ins.img");
+ for (MapleData itemFolder : itemsData.getChildren()) {
+ itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME")));
+ }
+ itemsData = stringData.getData("Pet.img");
+ for (MapleData itemFolder : itemsData.getChildren()) {
+ itemPairs.add(new Pair<>(Integer.parseInt(itemFolder.getName()), MapleDataTool.getString("name", itemFolder, "NO-NAME")));
+ }
+ return itemPairs;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/tools/HexTool.java b/tools/SpiderDropFetcher/src/tools/HexTool.java
new file mode 100644
index 0000000000..428baf3115
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/HexTool.java
@@ -0,0 +1,87 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools;
+
+import java.io.ByteArrayOutputStream;
+
+public class HexTool {
+ private static final char[] HEX = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ private static String toString(byte byteValue) {
+ int tmp = byteValue << 8;
+ char[] retstr = new char[]{HEX[(tmp >> 12) & 0x0F], HEX[(tmp >> 8) & 0x0F]};
+ return String.valueOf(retstr);
+ }
+
+ public static String toString(byte[] bytes) {
+ StringBuilder hexed = new StringBuilder();
+ for (int i = 0; i < bytes.length; i++) {
+ hexed.append(toString(bytes[i]));
+ hexed.append(' ');
+ }
+ return hexed.substring(0, hexed.length() - 1);
+ }
+
+ public static String toCompressedString(byte[] bytes) {
+ StringBuilder hexed = new StringBuilder();
+ for (int i = 0; i < bytes.length; i++) {
+ hexed.append(toString(bytes[i]));
+ }
+ return hexed.substring(0, hexed.length());
+ }
+
+ public static byte[] getByteArrayFromHexString(String hex) {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ int nexti = 0;
+ int nextb = 0;
+ boolean highoc = true;
+ outer:
+ for (;;) {
+ int number = -1;
+ while (number == -1) {
+ if (nexti == hex.length()) {
+ break outer;
+ }
+ char chr = hex.charAt(nexti);
+ if (chr >= '0' && chr <= '9') {
+ number = chr - '0';
+ } else if (chr >= 'a' && chr <= 'f') {
+ number = chr - 'a' + 10;
+ } else if (chr >= 'A' && chr <= 'F') {
+ number = chr - 'A' + 10;
+ } else {
+ number = -1;
+ }
+ nexti++;
+ }
+ if (highoc) {
+ nextb = number << 4;
+ highoc = false;
+ } else {
+ nextb |= number;
+ highoc = true;
+ baos.write(nextb);
+ }
+ }
+ return baos.toByteArray();
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/tools/Pair.java b/tools/SpiderDropFetcher/src/tools/Pair.java
new file mode 100644
index 0000000000..2a9864ae5f
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/Pair.java
@@ -0,0 +1,123 @@
+package tools;
+
+/*
+This file is part of the OdinMS Maple Story Server
+Copyright (C) 2008 ~ 2010 Patrick Huy
+Matthias Butz
+Jan Christian Meyer
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU Affero General Public License version 3
+as published by the Free Software Foundation. You may not use, modify
+or distribute this program under any other version of the
+GNU Affero General Public License.
+
+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 Affero General Public License for more details.
+
+You should have received a copy of the GNU Affero General Public License
+along with this program. If not, see .
+ */
+
+
+/**
+ * Represents a pair of values.
+ *
+ * @author Frz
+ * @since Revision 333
+ * @version 1.0
+ *
+ * @param The type of the left value.
+ * @param The type of the right value.
+ */
+public class Pair {
+
+ public E left;
+ public F right;
+
+ /**
+ * Class constructor - pairs two objects together.
+ *
+ * @param left The left object.
+ * @param right The right object.
+ */
+ public Pair(E left, F right) {
+ this.left = left;
+ this.right = right;
+ }
+
+ /**
+ * Gets the left value.
+ *
+ * @return The left value.
+ */
+ public E getLeft() {
+ return left;
+ }
+
+ /**
+ * Gets the right value.
+ *
+ * @return The right value.
+ */
+ public F getRight() {
+ return right;
+ }
+
+ /**
+ * Turns the pair into a string.
+ *
+ * @return Each value of the pair as a string joined by a colon.
+ */
+ @Override
+ public String toString() {
+ return left.toString() + ":" + right.toString();
+ }
+
+ /**
+ * Gets the hash code of this pair.
+ */
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + ((left == null) ? 0 : left.hashCode());
+ result = prime * result + ((right == null) ? 0 : right.hashCode());
+ return result;
+ }
+
+ /**
+ * Checks to see if two pairs are equal.
+ */
+ @SuppressWarnings("unchecked")
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final Pair other = (Pair) obj;
+ if (left == null) {
+ if (other.left != null) {
+ return false;
+ }
+ } else if (!left.equals(other.left)) {
+ return false;
+ }
+ if (right == null) {
+ if (other.right != null) {
+ return false;
+ }
+ } else if (!right.equals(other.right)) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/ByteArrayByteStream.java b/tools/SpiderDropFetcher/src/tools/data/input/ByteArrayByteStream.java
new file mode 100644
index 0000000000..eac7de21ea
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/ByteArrayByteStream.java
@@ -0,0 +1,72 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+import java.io.IOException;
+import tools.HexTool;
+
+public class ByteArrayByteStream implements SeekableInputStreamBytestream {
+ private int pos = 0;
+ private long bytesRead = 0;
+ private byte[] arr;
+
+ public ByteArrayByteStream(byte[] arr) {
+ this.arr = arr;
+ }
+
+ @Override
+ public long getPosition() {
+ return pos;
+ }
+
+ @Override
+ public void seek(long offset) throws IOException {
+ pos = (int) offset;
+ }
+
+ @Override
+ public long getBytesRead() {
+ return bytesRead;
+ }
+
+ @Override
+ public int readByte() {
+ bytesRead++;
+ return ((int) arr[pos++]) & 0xFF;
+ }
+
+ @Override
+ public String toString() {
+ String nows = "kevintjuh93 pwns";//I lol'd
+ if (arr.length - pos > 0) {
+ byte[] now = new byte[arr.length - pos];
+ System.arraycopy(arr, pos, now, 0, arr.length - pos);
+ nows = HexTool.toString(now);
+ }
+ return "All: " + HexTool.toString(arr) + "\nNow: " + nows;
+ }
+
+ @Override
+ public long available() {
+ return arr.length - pos;
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/ByteInputStream.java b/tools/SpiderDropFetcher/src/tools/data/input/ByteInputStream.java
new file mode 100644
index 0000000000..107f71843e
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/ByteInputStream.java
@@ -0,0 +1,35 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+/**
+ * Represents an abstract stream of bytes.
+ *
+ * @author Frz
+ * @version 1.0
+ * @since Revision 323
+ */
+public interface ByteInputStream {
+ int readByte();
+ long getBytesRead();
+ long available();
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/GenericLittleEndianAccessor.java b/tools/SpiderDropFetcher/src/tools/data/input/GenericLittleEndianAccessor.java
new file mode 100644
index 0000000000..d08a9b8374
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/GenericLittleEndianAccessor.java
@@ -0,0 +1,239 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+import java.awt.Point;
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Provides a generic interface to a Little Endian stream of bytes.
+ *
+ * @version 1.0
+ * @author Frz
+ * @since Revision 323
+ */
+public class GenericLittleEndianAccessor implements LittleEndianAccessor {
+ private ByteInputStream bs;
+
+ /**
+ * Class constructor - Wraps the accessor around a stream of bytes.
+ *
+ * @param bs The byte stream to wrap the accessor around.
+ */
+ public GenericLittleEndianAccessor(ByteInputStream bs) {
+ this.bs = bs;
+ }
+
+ /**
+ * Read a single byte from the stream.
+ *
+ * @return The byte read.
+ * @see tools.data.input.ByteInputStream#readByte
+ */
+ @Override
+ public byte readByte() {
+ return (byte) bs.readByte();
+ }
+
+ /**
+ * Reads an integer from the stream.
+ *
+ * @return The integer read.
+ */
+ @Override
+ public int readInt() {
+ return bs.readByte() + (bs.readByte() << 8) + (bs.readByte() << 16) + (bs.readByte() << 24);
+ }
+
+ /**
+ * Reads a short integer from the stream.
+ *
+ * @return The short read.
+ */
+ @Override
+ public short readShort() {
+ return (short) (bs.readByte() + (bs.readByte() << 8));
+ }
+
+ /**
+ * Reads a single character from the stream.
+ *
+ * @return The character read.
+ */
+ @Override
+ public char readChar() {
+ return (char) readShort();
+ }
+
+ /**
+ * Reads a long integer from the stream.
+ *
+ * @return The long integer read.
+ */
+ @Override
+ public long readLong() {
+ long byte1 = bs.readByte();
+ long byte2 = bs.readByte();
+ long byte3 = bs.readByte();
+ long byte4 = bs.readByte();
+ long byte5 = bs.readByte();
+ long byte6 = bs.readByte();
+ long byte7 = bs.readByte();
+ long byte8 = bs.readByte();
+ return (byte8 << 56) + (byte7 << 48) + (byte6 << 40) + (byte5 << 32) + (byte4 << 24) + (byte3 << 16) + (byte2 << 8) + byte1;
+ }
+
+ /**
+ * Reads a floating point integer from the stream.
+ *
+ * @return The float-type integer read.
+ */
+ @Override
+ public float readFloat() {
+ return Float.intBitsToFloat(readInt());
+ }
+
+ /**
+ * Reads a double-precision integer from the stream.
+ *
+ * @return The double-type integer read.
+ */
+ @Override
+ public double readDouble() {
+ return Double.longBitsToDouble(readLong());
+ }
+
+ /**
+ * Reads an ASCII string from the stream with length
n.
+ *
+ * @param n Number of characters to read.
+ * @return The string read.
+ */
+ public final String readAsciiString(int n) {
+ char ret[] = new char[n];
+ for (int x = 0; x < n; x++) {
+ ret[x] = (char) readByte();
+ }
+ return String.valueOf(ret);
+ }
+
+ /**
+ * Reads a null-terminated string from the stream.
+ *
+ * @return The string read.
+ */
+ public final String readNullTerminatedAsciiString() {
+ ByteArrayOutputStream baos = new ByteArrayOutputStream();
+ byte b;
+ while (true) {
+ b = readByte();
+ if (b == 0) {
+ break;
+ }
+ baos.write(b);
+ }
+ byte[] buf = baos.toByteArray();
+ char[] chrBuf = new char[buf.length];
+ for (int x = 0; x < buf.length; x++) {
+ chrBuf[x] = (char) buf[x];
+ }
+ return String.valueOf(chrBuf);
+ }
+
+ /**
+ * Gets the number of bytes read from the stream so far.
+ *
+ * @return A long integer representing the number of bytes read.
+ * @see tools.data.input.ByteInputStream#getBytesRead()
+ */
+ public long getBytesRead() {
+ return bs.getBytesRead();
+ }
+
+ /**
+ * Reads a MapleStory convention lengthed ASCII string.
+ * This consists of a short integer telling the length of the string,
+ * then the string itself.
+ *
+ * @return The string read.
+ */
+ @Override
+ public String readMapleAsciiString() {
+ return readAsciiString(readShort());
+ }
+
+ /**
+ * Reads num bytes off the stream.
+ *
+ * @param num The number of bytes to read.
+ * @return An array of bytes with the length of num
+ */
+ @Override
+ public byte[] read(int num) {
+ byte[] ret = new byte[num];
+ for (int x = 0; x < num; x++) {
+ ret[x] = readByte();
+ }
+ return ret;
+ }
+
+ /**
+ * Reads a MapleStory Position information.
+ * This consists of 2 short integer.
+ *
+ * @return The Position read.
+ */
+ @Override
+ public final Point readPos() {
+ final int x = readShort();
+ final int y = readShort();
+ return new Point(x, y);
+ }
+
+ /**
+ * Skips the current position of the stream num bytes ahead.
+ *
+ * @param num Number of bytes to skip.
+ */
+ @Override
+ public void skip(int num) {
+ for (int x = 0; x < num; x++) {
+ readByte();
+ }
+ }
+
+ /**
+ * @see tools.data.input.ByteInputStream#available
+ */
+ @Override
+ public long available() {
+ return bs.available();
+ }
+
+ /**
+ * @see java.lang.Object#toString
+ */
+ @Override
+ public String toString() {
+ return bs.toString();
+ }
+}
\ No newline at end of file
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java b/tools/SpiderDropFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java
new file mode 100644
index 0000000000..fdd147d796
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/GenericSeekableLittleEndianAccessor.java
@@ -0,0 +1,91 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+import java.io.IOException;
+
+/**
+ * Provides an abstract accessor to a generic Little Endian byte stream. This
+ * accessor is seekable.
+ *
+ * @author Frz
+ * @version 1.0
+ * @since Revision 323
+ * @see tools.data.input.GenericLittleEndianAccessor
+ */
+public class GenericSeekableLittleEndianAccessor extends GenericLittleEndianAccessor implements SeekableLittleEndianAccessor {
+ private SeekableInputStreamBytestream bs;
+
+ /**
+ * Class constructor
+ * Provide a seekable input stream to wrap this object around.
+ *
+ * @param bs The byte stream to wrap this around.
+ */
+ public GenericSeekableLittleEndianAccessor(SeekableInputStreamBytestream bs) {
+ super(bs);
+ this.bs = bs;
+ }
+
+ /**
+ * Seek the pointer to offset
+ *
+ * @param offset The offset to seek to.
+ * @see tools.data.input.SeekableInputStreamBytestream#seek
+ */
+ @Override
+ public void seek(long offset) {
+ try {
+ bs.seek(offset);
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println("Seek failed " + e);
+ }
+ }
+
+ /**
+ * Get the current position of the pointer.
+ *
+ * @return The current position of the pointer as a long integer.
+ * @see tools.data.input.SeekableInputStreamBytestream#getPosition
+ */
+ @Override
+ public long getPosition() {
+ try {
+ return bs.getPosition();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println("getPosition failed" + e);
+ return -1;
+ }
+ }
+
+ /**
+ * Skip num number of bytes in the stream.
+ *
+ * @param num The number of bytes to skip.
+ */
+ @Override
+ public void skip(int num) {
+ seek(getPosition() + num);
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/InputStreamByteStream.java b/tools/SpiderDropFetcher/src/tools/data/input/InputStreamByteStream.java
new file mode 100644
index 0000000000..70aef3489f
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/InputStreamByteStream.java
@@ -0,0 +1,93 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Provides an abstract wrapper to a stream of bytes.
+ *
+ * @author Frz
+ * @version 1.0
+ * @since Revision 323
+ */
+public class InputStreamByteStream implements ByteInputStream {
+ private InputStream is;
+ private long read = 0;
+
+ /**
+ * Class constructor.
+ * Provide an input stream to wrap this around.
+ *
+ * @param is The input stream to wrap this object around.
+ */
+ public InputStreamByteStream(InputStream is) {
+ this.is = is;
+ }
+
+ /**
+ * Reads the next byte from the stream.
+ *
+ * @return Then next byte in the stream.
+ */
+ @Override
+ public int readByte() {
+ int temp;
+ try {
+ temp = is.read();
+ if (temp == -1) {
+ throw new RuntimeException("EOF");
+ }
+ read++;
+ return temp;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Gets the number of bytes read from the stream.
+ *
+ * @return The number of bytes read as a long integer.
+ */
+ @Override
+ public long getBytesRead() {
+ return read;
+ }
+
+ /**
+ * Returns the number of bytes left in the stream.
+ *
+ * @return The number of bytes available for reading as a long integer.
+ */
+ @Override
+ public long available() {
+ try {
+ return is.available();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println("ERROR" + e);
+ return 0;
+ }
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/LittleEndianAccessor.java b/tools/SpiderDropFetcher/src/tools/data/input/LittleEndianAccessor.java
new file mode 100644
index 0000000000..f991dbf537
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/LittleEndianAccessor.java
@@ -0,0 +1,45 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+import java.awt.Point;
+
+/**
+ * @author Frz
+ */
+public interface LittleEndianAccessor {
+ byte readByte();
+ char readChar();
+ short readShort();
+ int readInt();
+ Point readPos();
+ long readLong();
+ void skip(int num);
+ byte[] read(int num);
+ float readFloat();
+ double readDouble();
+ String readAsciiString(int n);
+ String readNullTerminatedAsciiString();
+ String readMapleAsciiString();
+ long getBytesRead();
+ long available();
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/RandomAccessByteStream.java b/tools/SpiderDropFetcher/src/tools/data/input/RandomAccessByteStream.java
new file mode 100644
index 0000000000..c0004be17f
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/RandomAccessByteStream.java
@@ -0,0 +1,84 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * Provides an abstract layer to a byte stream. This layer can be accessed
+ * randomly.
+ *
+ * @author Frz
+ * @version 1.0
+ * @since Revision 323
+ */
+public class RandomAccessByteStream implements SeekableInputStreamBytestream {
+ private RandomAccessFile raf;
+ private long read = 0;
+
+ public RandomAccessByteStream(RandomAccessFile raf) {
+ super();
+ this.raf = raf;
+ }
+
+ @Override
+ public int readByte() {
+ int temp;
+ try {
+ temp = raf.read();
+ if (temp == -1) {
+ throw new RuntimeException("EOF");
+ }
+ read++;
+ return temp;
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public void seek(long offset) throws IOException {
+ raf.seek(offset);
+ }
+
+ @Override
+ public long getPosition() throws IOException {
+ return raf.getFilePointer();
+ }
+
+ @Override
+ public long getBytesRead() {
+ return read;
+ }
+
+ @Override
+ public long available() {
+ try {
+ return raf.length() - raf.getFilePointer();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.out.println("ERROR " + e);
+ return 0;
+ }
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/SeekableInputStreamBytestream.java b/tools/SpiderDropFetcher/src/tools/data/input/SeekableInputStreamBytestream.java
new file mode 100644
index 0000000000..f4922dc876
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/SeekableInputStreamBytestream.java
@@ -0,0 +1,51 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+import java.io.IOException;
+
+/**
+ * Provides an abstract interface to a stream of bytes. This stream can be
+ * seeked.
+ *
+ * @author Frz
+ * @version 1.0
+ * @since 299
+ */
+public interface SeekableInputStreamBytestream extends ByteInputStream {
+ /**
+ * Seeks the stream by the specified offset.
+ *
+ * @param offset
+ * Number of bytes to seek.
+ * @throws IOException
+ */
+ void seek(long offset) throws IOException;
+
+ /**
+ * Gets the current position of the stream.
+ *
+ * @return The stream position as a long integer.
+ * @throws IOException
+ */
+ long getPosition() throws IOException;
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java b/tools/SpiderDropFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java
new file mode 100644
index 0000000000..16b2317f7a
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/input/SeekableLittleEndianAccessor.java
@@ -0,0 +1,27 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.input;
+
+public interface SeekableLittleEndianAccessor extends LittleEndianAccessor {
+ void seek(long offset);
+ long getPosition();
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/output/BAOSByteOutputStream.java b/tools/SpiderDropFetcher/src/tools/data/output/BAOSByteOutputStream.java
new file mode 100644
index 0000000000..80cbc9301e
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/output/BAOSByteOutputStream.java
@@ -0,0 +1,56 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see .
+*/
+package tools.data.output;
+
+import java.io.ByteArrayOutputStream;
+
+/**
+ * Uses a byte array to output a stream of bytes.
+ *
+ * @author Frz
+ * @version 1.0
+ * @since Revision 352
+ */
+class BAOSByteOutputStream implements ByteOutputStream {
+ private ByteArrayOutputStream baos;
+
+ /**
+ * Class constructor - Wraps the stream around a Java BAOS.
+ *
+ * @param baos The ByteArrayOutputStream to wrap this around.
+ */
+ BAOSByteOutputStream(ByteArrayOutputStream baos) {
+ super();
+ this.baos = baos;
+ }
+
+ /**
+ * Writes a byte to the stream.
+ *
+ * @param b The byte to write to the stream.
+ * @see tools.data.output.ByteOutputStream#writeByte(byte)
+ */
+ @Override
+ public void writeByte(byte b) {
+ baos.write(b);
+ }
+}
diff --git a/tools/SpiderDropFetcher/src/tools/data/output/ByteOutputStream.java b/tools/SpiderDropFetcher/src/tools/data/output/ByteOutputStream.java
new file mode 100644
index 0000000000..0df7ca7753
--- /dev/null
+++ b/tools/SpiderDropFetcher/src/tools/data/output/ByteOutputStream.java
@@ -0,0 +1,38 @@
+/*
+ This file is part of the OdinMS Maple Story Server
+ Copyright (C) 2008 Patrick Huy
+ Matthias Butz
+ Jan Christian Meyer
+
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU Affero General Public License as
+ published by the Free Software Foundation version 3 as published by
+ the Free Software Foundation. You may not use, modify or distribute
+ this program under any other version of the GNU Affero General Public
+ License.
+
+ 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 Affero General Public License for more details.
+
+ You should have received a copy of the GNU Affero General Public License
+ along with this program. If not, see