Improved login phase + C. scheduler & EXP share & MoveLifeHandler fix
Refactored login system, caching account data, merging some queries and using way less DB queries on login. Server now uses associative tables for character-account and character-world, lowering considerably usage of some DB queries. Fixed getPartyMembersOnSameMap method trying to access disconnected members, promptly throwing nulls. Improved EXP distribution system, now crediting damage-contributing EXP to the party when the player is not present on the map. Improved the "View-all-chars" feature mechanics, not so often disconnecting players for server response timeout anymore. Improved Mystic Doors mechanics, now correctly spawning party players at actual door location on the off-town map. Fixed "fly" command not working properly. All characters of that account are able to use this mechanic (client session limitation). Fixed a critical deadlock issue on the new channel scheduler system. Fixed some mobs not using skills, issue brought on the latest MoveLifeHandler update. Improved slightly skill/movement synergy on the MoveLifeHandler responses. GMs no longer creates Hall-of-fame PlayerNPCs when reaching max class level. Fixed monsterValue script method being triggered multiple times for party members. Fixed pinkbean not dropping items inside expedition. Moved "recharge" command from Donator to JrGM.
This commit is contained in:
+1
-1
@@ -21,7 +21,6 @@ Missing features list:
|
||||
- Miniroom tooltips (such as number of players in store/host awaiting game) not showing up properly.
|
||||
- Change name/World transfer.
|
||||
- Some pirate skills doesn't work for 3rd parties.
|
||||
- Medal quests.
|
||||
- Cache frequently used SQL data.
|
||||
---------------------------
|
||||
|
||||
@@ -35,6 +34,7 @@ Missing features list:
|
||||
|
||||
---------------------------
|
||||
** Quest **
|
||||
- Family & Medal quests.
|
||||
---------------------------
|
||||
|
||||
|
||||
|
||||
+23
-1
@@ -6,7 +6,7 @@
|
||||
Abdula -> 9209000
|
||||
|
||||
CUSTOM NPC SHOPS:
|
||||
Spindle -> 9201082
|
||||
Asia -> 2082014
|
||||
T-1337 -> 9201101
|
||||
|
||||
LOGS:
|
||||
@@ -1108,3 +1108,25 @@ Melhorado liberação de recursos ao finalizar objeto MapleMonster.
|
||||
Adicionado server flag que permite usar clean slates mesmo em equipamentos sem slots vazios (onde falhou o scroll).
|
||||
Corrigido um possível ponto crítico de deadlock com MapleServerHandler.
|
||||
Adicionado portal SFX para vários portais scriptados que ainda faltavam o efeito.
|
||||
|
||||
08 - 10 Julho 2018,
|
||||
Refatorado sistema de login, agora utilizando caches para manter dados recorrentes, diminuindo consideravelmente consultas a DB.
|
||||
Server agora usa registros associativos de conta, personagem e mundo em seus objetos, diminuindo bastante o uso da DB e, por consequência, uma melhor fluidez em demais processos, mais notáveis aqueles referentes a fase de login.
|
||||
Corrigido metodo que localiza membros de party no mesmo mapa tentando acessar possível campo "map", que fica nulo para jogadores desconectados.
|
||||
|
||||
11 - 12 Julho 2018,
|
||||
Otimizado chamadas a DB na fase de login, agora utilizando uma mesma query para recuperar dados de todos os personagens de uma dada conta (diminuindo drasticamente a quantidade de consultas a DB).
|
||||
Melhorado sistema de distribuicao de EXP, agora creditando EXP aos membros de party presentes no mapa mesmo quando o jogador que contribuiu com a ação nao se encontra presente no mesmo.
|
||||
Corrigido jogadores nao podendo usar portais apos logarem via "view-all-chars".
|
||||
Corrigido cash shop inventory de Cygnus e Aran nao propagando itens de cash comprado para outros personagens de mesma classe base da mesma conta.
|
||||
Melhorado mecânica de Magic Doors, permitindo jogadores serem spawnados corretamente ao atravessar portais quando em party.
|
||||
Corrigido comando "fly" nao atuando corretamente. Todos os personagens da conta poderao usar desta mecânica.
|
||||
Corrigido deadlock crítico introduzido no sistema abstrato de temporizadores em channels.
|
||||
Corrigido alguns mobs nao lançando skills, problema introduzido na última atualização do MoveLifeHandler.
|
||||
Ajustado MoveLifeHandler para melhor aplicar as skills/movimentações dos mobs.
|
||||
GMs nao mais geram playernpcs ao atingir level 200 (agora somente via comando).
|
||||
Corrigido função monsterValue dos scripts de eventos sendo processado múltiplas vezes no cenário onde jogadores estao em party.
|
||||
Corrigido pinkbean nao dropando itens dentro da expedição.
|
||||
|
||||
14 Julho 2018,
|
||||
Movido "recharge" de Donator para JrGM.
|
||||
@@ -162,6 +162,12 @@ function playerRevive(eim, player) {
|
||||
return true;
|
||||
}
|
||||
|
||||
function monsterRevive(eim, mob) {
|
||||
if(isPinkBean(mob)) {
|
||||
mob.enableDrops();
|
||||
}
|
||||
}
|
||||
|
||||
function playerDisconnected(eim, player) {
|
||||
if (eim.isEventTeamLackingNow(true, minPlayers, player)) {
|
||||
eim.dropMessage(5, "[Expedition] Either the leader has quit the expedition or there is no longer the minimum number of members required to continue it.");
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
importPackage(Packages.server.life);
|
||||
|
||||
function start(ms) {
|
||||
var pos = new java.awt.Point(842, 0);
|
||||
var mobId = 9400633;
|
||||
var mobName = "Astaroth";
|
||||
|
||||
var player = ms.getPlayer();
|
||||
var map = player.getMap();
|
||||
|
||||
if(map.getMonsterById(mobId) != null){
|
||||
return;
|
||||
}
|
||||
|
||||
map.spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(mobId), pos);
|
||||
player.message(mobName + " has appeared!");
|
||||
}
|
||||
@@ -50,7 +50,7 @@ function start() {
|
||||
if (cm.getLevel() >= 8 && cm.canGetFirstJob(jobType))
|
||||
cm.sendNext("Want to be a magician? There are some standards to meet. because we can't just accept EVERYONE in... #bYour level should be at least 8#k, with getting INT as your top priority. Let's see.");
|
||||
else {
|
||||
cm.sendOk("Train a bit more until you reach #blevel 10, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rMagician#k.");
|
||||
cm.sendOk("Train a bit more until you reach #blevel 8, " + cm.getFirstJobStatRequirement(jobType) + "#k and I can show you the way of the #rMagician#k.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else if (cm.getLevel() >= 30 && cm.getJobId() == 200) {
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
/* A Familiar Lady
|
||||
Hidden Street : Gloomy Forest (922220000)
|
||||
*/
|
||||
|
||||
var status;
|
||||
|
||||
function start() {
|
||||
status = -1;
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == -1) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
if (mode == 0 && type > 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (mode == 1)
|
||||
status++;
|
||||
else
|
||||
status--;
|
||||
|
||||
if(status == 0) {
|
||||
if(cm.getQuestProgress(23647, 0) != 0) {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!cm.haveItem(4031793, 1)) {
|
||||
cm.sendOk("Umm... Hey... Would you help me find a #bsoft and shiny silver fur#k that I lost on the woods? I need it, I need it, I need it sooooo much!");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
cm.sendYesNo("Hey... Umm... Would you help me find a #bsoft and shiny silver fur#k that I lost on the woods? I need it, I need it, I need it sooooo much! ... Oh you found it!!! Will you give it to me?");
|
||||
} else if(status == 1) {
|
||||
cm.sendNext("Teehehee~ That's your reward for taking it from me, serves you well.");
|
||||
cm.gainItem(4031793, -1);
|
||||
cm.gainFame(-5);
|
||||
cm.setQuestProgress(23647, 0, 1);
|
||||
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -0,0 +1,45 @@
|
||||
var map = 677000010;
|
||||
var quest = 28283;
|
||||
var status = -1;
|
||||
var inHuntingGround;
|
||||
|
||||
function start(mode, type, selection) {
|
||||
inHuntingGround = (cm.getMapId() >= 677000010 && cm.getMapId() <= 677000012);
|
||||
action(1, 0, 0);
|
||||
}
|
||||
|
||||
function action(mode, type, selection) {
|
||||
if (mode == 1) {
|
||||
status++;
|
||||
} else {
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
if (status == 0) {
|
||||
if(!inHuntingGround) {
|
||||
if (cm.isQuestStarted(quest)) {
|
||||
if(!cm.getPlayer().haveItemEquipped(1003036)) {
|
||||
cm.sendOk("The path ahead has a weird stench... Equip the #rgas mask#k before entering.");
|
||||
cm.dispose();
|
||||
return;
|
||||
}
|
||||
|
||||
cm.sendYesNo("Would you like to move to #b#m" + map + "##k?");
|
||||
} else {
|
||||
cm.sendOk("The entrance is blocked by a strange force.");
|
||||
cm.dispose();
|
||||
}
|
||||
} else {
|
||||
if(cm.getMapId() == 677000011) {
|
||||
map = 677000012;
|
||||
cm.sendYesNo("Would you like to move to #b#m" + map + "##k?");
|
||||
} else {
|
||||
map = 105050400;
|
||||
cm.sendYesNo("Would you like to #bexit this place#k?");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
cm.warp(map, 0);
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
@@ -156,6 +156,7 @@ function writeFeatureTab_Serverpotentials() {
|
||||
addFeature("Custom jail system.");
|
||||
addFeature("Custom buyback system, uses mesos / NX, via MTS.");
|
||||
addFeature("Delete Character.");
|
||||
addFeature("Smooth view-all-char, now showing all account chars.");
|
||||
addFeature("Autosaver (periodically saves player's data on DB).");
|
||||
addFeature("Fixed and randomized HP/MP growth rate available.");
|
||||
addFeature("Prevented 'NPC gone after some uptime' issue.");
|
||||
@@ -199,6 +200,7 @@ function writeFeatureTab_Project() {
|
||||
addFeature("Uncovered many opcodes throughout the source.");
|
||||
addFeature("Reviewed many Java aspects that needed attention.");
|
||||
addFeature("Reviewed SQL data, eliminating duplicated entries.");
|
||||
addFeature("Improved login phase, using cache over DB queries.");
|
||||
addFeature("Protected many flaws with login management system.");
|
||||
addFeature("Developed many survey tools for content profiling.");
|
||||
addFeature("ThreadTracker: runtime tool for deadlock detection.");
|
||||
|
||||
@@ -162,6 +162,7 @@ function writeHeavenMSCommandsLv2() { //JrGM
|
||||
comm_cursor = comm_lv2;
|
||||
desc_cursor = desc_lv2;
|
||||
|
||||
addCommand("recharge", "");
|
||||
addCommand("whereami", "");
|
||||
addCommand("hide", "");
|
||||
addCommand("unhide", "");
|
||||
@@ -205,7 +206,6 @@ function writeHeavenMSCommandsLv1() { //Donator
|
||||
addCommand("whodrops", "");
|
||||
addCommand("buffme", "");
|
||||
addCommand("goto", "");
|
||||
addCommand("recharge", "");
|
||||
}
|
||||
|
||||
function writeHeavenMSCommandsLv0() { //Common
|
||||
|
||||
@@ -1,4 +1,10 @@
|
||||
function enter(pi) {
|
||||
if(!(pi.isQuestStarted(3647) && pi.haveItem(4031793, 1))) {
|
||||
pi.playPortalSound(); pi.warp(222010200, "east00");
|
||||
} else {
|
||||
if(!pi.isQuestStarted(23647)) pi.forceStartQuest(23647);
|
||||
pi.playPortalSound(); pi.warp(922220000, "east00");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+8
-8
@@ -19288,8 +19288,7 @@ USE `heavenms`;
|
||||
(4230117, 2382055, 1, 1, 0, 10000),
|
||||
(4230118, 2382067, 1, 1, 0, 10000),
|
||||
(4240000, 2383011, 1, 1, 0, 10000),
|
||||
(1210102, 4001354, 1, 1, 28209, 10000),
|
||||
(2110200, 4001368, 1, 1, 28258, 10000),
|
||||
(1210102, 4001354, 1, 1, 28209, 80000),
|
||||
(1110101, 4031773, 1, 1, 2145, 80000),
|
||||
(1130100, 4031773, 1, 1, 2145, 80000),
|
||||
(1140100, 4031773, 1, 1, 2145, 80000),
|
||||
@@ -19811,13 +19810,14 @@ USE `heavenms`;
|
||||
(9400120, 2290109, 1, 1, 0, 10000),
|
||||
(3210100, 4001000, 1, 1, 2017, 20000),
|
||||
(4300013, 4000538, 1, 1, 2288, 400000),
|
||||
(2130100, 4001367, 1, 1, 28257, 10000),
|
||||
(1110100, 4001369, 1, 1, 28259, 10000),
|
||||
(1210101, 4001370, 1, 1, 28260, 10000),
|
||||
(1110101, 4001371, 1, 1, 28261, 10000),
|
||||
(2130100, 4001367, 1, 1, 28257, 40000),
|
||||
(2110200, 4001368, 1, 1, 28258, 40000),
|
||||
(1110100, 4001369, 1, 1, 28259, 40000),
|
||||
(1210101, 4001370, 1, 1, 28260, 40000),
|
||||
(1110101, 4001371, 1, 1, 28261, 40000),
|
||||
(3300003, 4001317, 1, 1, 2326, 20000),
|
||||
(1210102, 4001364, 1, 1, 28192, 10000),
|
||||
(1120100, 4001365, 1, 1, 28192, 10000),
|
||||
(1210102, 4001364, 1, 1, 28192, 80000),
|
||||
(1120100, 4001365, 1, 1, 28192, 80000),
|
||||
(1210103, 4032137, 1, 1, 20711, 40000),
|
||||
(1210103, 4032139, 1, 1, 20713, 40000),
|
||||
(1210101, 4001357, 1, 1, 28244, 70000),
|
||||
|
||||
+134
-14
@@ -1350,7 +1350,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
|
||||
eventChangedMap(target.getId());
|
||||
MapleMap to = getWarpMap(target.getId());
|
||||
changeMapInternal(to, pos, MaplePacketCreator.getWarpToMap(to, 0x80, this));//Position :O (LEFT)
|
||||
changeMapInternal(to, pos, MaplePacketCreator.getWarpToMap(to, 0x80, pos, this));
|
||||
canWarpMap = false;
|
||||
|
||||
canWarpCounter--;
|
||||
@@ -1394,6 +1394,25 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Integer> getLastVisitedMapids() {
|
||||
List<Integer> lastVisited = new ArrayList<>(5);
|
||||
|
||||
petLock.lock();
|
||||
try {
|
||||
for(WeakReference<MapleMap> lv : lastVisitedMaps) {
|
||||
MapleMap lvm = lv.get();
|
||||
|
||||
if(lvm != null) {
|
||||
lastVisited.add(lvm.getId());
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
petLock.unlock();
|
||||
}
|
||||
|
||||
return lastVisited;
|
||||
}
|
||||
|
||||
public void updateMapDropsUponPartyOperation(List<MapleCharacter> exPartyMembers) {
|
||||
List<WeakReference<MapleMap>> mapids;
|
||||
|
||||
@@ -1890,7 +1909,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
|
||||
public static boolean deleteCharFromDB(MapleCharacter player, int senderAccId) {
|
||||
int cid = player.getId();
|
||||
if(!Server.getInstance().haveCharacterid(senderAccId, cid)) {
|
||||
if(!Server.getInstance().haveCharacterEntry(senderAccId, cid)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2050,7 +2069,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
|
||||
con.close();
|
||||
Server.getInstance().deleteCharacterid(accId, cid);
|
||||
Server.getInstance().deleteCharacterEntry(accId, cid);
|
||||
return true;
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
@@ -4583,7 +4602,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
if(party != null) {
|
||||
for(MaplePartyCharacter partyMembers: party.getMembers()) {
|
||||
MapleCharacter chr = partyMembers.getPlayer();
|
||||
if(chr.getMap().hashCode() == thisMapHash && chr.isLoggedinWorld()) list.add(chr);
|
||||
MapleMap chrMap = chr.getMap();
|
||||
if(chrMap != null && chrMap.hashCode() == thisMapHash && chr.isLoggedinWorld()) list.add(chr);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
@@ -5285,15 +5305,15 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isCygnus() {
|
||||
return getJobType() == 1;
|
||||
}
|
||||
|
||||
public boolean isGmJob() {
|
||||
int jn = job.getJobNiche();
|
||||
return jn >= 8 && jn <= 9;
|
||||
}
|
||||
|
||||
public boolean isCygnus() {
|
||||
return getJobType() == 1;
|
||||
}
|
||||
|
||||
public boolean isAran() {
|
||||
return job.getId() >= 2000 && job.getId() <= 2112;
|
||||
}
|
||||
@@ -5423,7 +5443,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
maxmp = Math.min(30000, maxmp);
|
||||
if (level == 200) {
|
||||
exp.set(0);
|
||||
if(ServerConstants.PLAYERNPC_AUTODEPLOY) MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), this);
|
||||
if(ServerConstants.PLAYERNPC_AUTODEPLOY && !this.isGM()) MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), this);
|
||||
}
|
||||
recalcLocalStats();
|
||||
hp = localmaxhp;
|
||||
@@ -5787,6 +5807,104 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
public static MapleCharacter loadCharacterEntryFromDB(ResultSet rs, List<Item> equipped) {
|
||||
MapleCharacter ret = new MapleCharacter();
|
||||
|
||||
try {
|
||||
ret.accountid = rs.getInt("accountid");
|
||||
ret.id = rs.getInt("id");
|
||||
ret.name = rs.getString("name");
|
||||
ret.gender = rs.getInt("gender");
|
||||
ret.skinColor = MapleSkinColor.getById(rs.getInt("skincolor"));
|
||||
ret.face = rs.getInt("face");
|
||||
ret.hair = rs.getInt("hair");
|
||||
|
||||
// skipping pets, probably unneeded here
|
||||
|
||||
ret.level = rs.getInt("level");
|
||||
ret.job = MapleJob.getById(rs.getInt("job"));
|
||||
ret.str = rs.getInt("str");
|
||||
ret.dex = rs.getInt("dex");
|
||||
ret.int_ = rs.getInt("int");
|
||||
ret.luk = rs.getInt("luk");
|
||||
ret.hp = rs.getInt("hp");
|
||||
ret.maxhp = rs.getInt("maxhp");
|
||||
ret.mp = rs.getInt("mp");
|
||||
ret.maxmp = rs.getInt("maxmp");
|
||||
|
||||
ret.remainingAp = rs.getInt("ap");
|
||||
String[] skillPoints = rs.getString("sp").split(",");
|
||||
for (int i = 0; i < ret.remainingSp.length; i++) {
|
||||
ret.remainingSp[i] = Integer.parseInt(skillPoints[i]);
|
||||
}
|
||||
|
||||
ret.exp.set(rs.getInt("exp"));
|
||||
ret.fame = rs.getInt("fame");
|
||||
ret.gachaexp.set(rs.getInt("gachaexp"));
|
||||
ret.mapid = rs.getInt("map");
|
||||
ret.initialSpawnPoint = rs.getInt("spawnpoint");
|
||||
|
||||
ret.gmLevel = rs.getInt("gm");
|
||||
ret.world = rs.getByte("world");
|
||||
ret.rank = rs.getInt("rank");
|
||||
ret.rankMove = rs.getInt("rankMove");
|
||||
ret.jobRank = rs.getInt("jobRank");
|
||||
ret.jobRankMove = rs.getInt("jobRankMove");
|
||||
|
||||
MapleInventory inv = ret.inventory[MapleInventoryType.EQUIPPED.ordinal()];
|
||||
for (Item item : equipped) {
|
||||
inv.addItemFromDB(item);
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public MapleCharacter generateCharacterEntry() {
|
||||
MapleCharacter ret = new MapleCharacter();
|
||||
|
||||
ret.accountid = this.getAccountID();
|
||||
ret.id = this.getId();
|
||||
ret.name = this.getName();
|
||||
ret.gender = this.getGender();
|
||||
ret.skinColor = this.getSkinColor();
|
||||
ret.face = this.getFace();
|
||||
ret.hair = this.getHair();
|
||||
|
||||
// skipping pets, probably unneeded here
|
||||
|
||||
ret.level = this.getLevel();
|
||||
ret.job = this.getJob();
|
||||
ret.str = this.getStr();
|
||||
ret.dex = this.getDex();
|
||||
ret.int_ = this.getInt();
|
||||
ret.luk = this.getLuk();
|
||||
ret.hp = this.getHp();
|
||||
ret.maxhp = this.getMaxHp();
|
||||
ret.mp = this.getMp();
|
||||
ret.maxmp = this.getMaxMp();
|
||||
ret.remainingAp = this.getRemainingAp();
|
||||
ret.remainingSp = this.getRemainingSps();
|
||||
ret.exp.set(this.getExp());
|
||||
ret.fame = this.getFame();
|
||||
ret.gachaexp.set(this.getGachaExp());
|
||||
ret.mapid = this.getMapId();
|
||||
ret.initialSpawnPoint = this.getInitialSpawnpoint();
|
||||
|
||||
ret.inventory[MapleInventoryType.EQUIPPED.ordinal()] = this.getInventory(MapleInventoryType.EQUIPPED);
|
||||
|
||||
ret.gmLevel = this.gmLevel();
|
||||
ret.world = this.getWorld();
|
||||
ret.rank = this.getRank();
|
||||
ret.rankMove = this.getRankMove();
|
||||
ret.jobRank = this.getJobRank();
|
||||
ret.jobRankMove = this.getJobRankMove();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static MapleCharacter loadCharFromDB(int charid, MapleClient client, boolean channelserver) throws SQLException {
|
||||
try {
|
||||
MapleCharacter ret = new MapleCharacter();
|
||||
@@ -6845,24 +6963,24 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
public void saveToDB() {
|
||||
public void saveCharToDB() {
|
||||
if(ServerConstants.USE_AUTOSAVE) {
|
||||
Runnable r = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
saveToDB(true);
|
||||
saveCharToDB(true);
|
||||
}
|
||||
};
|
||||
|
||||
Thread t = new Thread(r); //spawns a new thread to deal with this
|
||||
t.start();
|
||||
} else {
|
||||
saveToDB(true);
|
||||
saveCharToDB(true);
|
||||
}
|
||||
}
|
||||
|
||||
//ItemFactory saveItems and monsterbook.saveCards are the most time consuming here.
|
||||
public synchronized void saveToDB(boolean notAutosave) {
|
||||
public synchronized void saveCharToDB(boolean notAutosave) {
|
||||
if(!loggedIn) return;
|
||||
|
||||
Calendar c = Calendar.getInstance();
|
||||
@@ -6870,6 +6988,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
if(notAutosave) FilePrinter.print(FilePrinter.SAVING_CHARACTER, "Attempting to save " + name + " at " + c.getTime().toString());
|
||||
else FilePrinter.print(FilePrinter.AUTOSAVING_CHARACTER, "Attempting to autosave " + name + " at " + c.getTime().toString());
|
||||
|
||||
Server.getInstance().updateCharacterEntry(this);
|
||||
|
||||
Connection con = null;
|
||||
try {
|
||||
con = DatabaseConnection.getConnection();
|
||||
@@ -7693,7 +7813,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
|
||||
if (slots <= 96) {
|
||||
inventory[type].setSlotLimit(slots);
|
||||
|
||||
this.saveToDB();
|
||||
this.saveCharToDB();
|
||||
if (update) {
|
||||
client.announce(MaplePacketCreator.updateInventorySlotLimit(type, slots));
|
||||
}
|
||||
|
||||
@@ -841,7 +841,7 @@ public class MapleClient {
|
||||
|
||||
removePlayer();
|
||||
player.saveCooldowns();
|
||||
player.saveToDB(true);
|
||||
player.saveCharToDB(true);
|
||||
|
||||
clear();
|
||||
return;
|
||||
@@ -924,7 +924,7 @@ public class MapleClient {
|
||||
//getChannelServer().removePlayer(player); already being done
|
||||
|
||||
player.saveCooldowns();
|
||||
player.saveToDB(true);
|
||||
player.saveCharToDB(true);
|
||||
if (player != null) {//no idea, occur :(
|
||||
player.empty(false);
|
||||
}
|
||||
@@ -933,7 +933,7 @@ public class MapleClient {
|
||||
getChannelServer().removePlayer(player);
|
||||
|
||||
player.saveCooldowns();
|
||||
player.saveToDB();
|
||||
player.saveCharToDB();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -785,26 +785,6 @@ public class Commands {
|
||||
}
|
||||
break;
|
||||
|
||||
case "recharge":
|
||||
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
|
||||
for (Item torecharge : c.getPlayer().getInventory(MapleInventoryType.USE).list()) {
|
||||
if (ItemConstants.isThrowingStar(torecharge.getItemId())){
|
||||
torecharge.setQuantity(ii.getSlotMax(c, torecharge.getItemId()));
|
||||
c.getPlayer().forceUpdateItem(torecharge);
|
||||
} else if (ItemConstants.isArrow(torecharge.getItemId())){
|
||||
torecharge.setQuantity(ii.getSlotMax(c, torecharge.getItemId()));
|
||||
c.getPlayer().forceUpdateItem(torecharge);
|
||||
} else if (ItemConstants.isBullet(torecharge.getItemId())){
|
||||
torecharge.setQuantity(ii.getSlotMax(c, torecharge.getItemId()));
|
||||
c.getPlayer().forceUpdateItem(torecharge);
|
||||
} else if (ItemConstants.isConsumable(torecharge.getItemId())){
|
||||
torecharge.setQuantity(ii.getSlotMax(c, torecharge.getItemId()));
|
||||
c.getPlayer().forceUpdateItem(torecharge);
|
||||
}
|
||||
}
|
||||
player.dropMessage(5, "USE Recharged.");
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
@@ -818,6 +798,26 @@ public class Commands {
|
||||
Skill skill;
|
||||
|
||||
switch(sub[0]) {
|
||||
case "recharge":
|
||||
MapleItemInformationProvider mii = MapleItemInformationProvider.getInstance();
|
||||
for (Item torecharge : c.getPlayer().getInventory(MapleInventoryType.USE).list()) {
|
||||
if (ItemConstants.isThrowingStar(torecharge.getItemId())){
|
||||
torecharge.setQuantity(mii.getSlotMax(c, torecharge.getItemId()));
|
||||
c.getPlayer().forceUpdateItem(torecharge);
|
||||
} else if (ItemConstants.isArrow(torecharge.getItemId())){
|
||||
torecharge.setQuantity(mii.getSlotMax(c, torecharge.getItemId()));
|
||||
c.getPlayer().forceUpdateItem(torecharge);
|
||||
} else if (ItemConstants.isBullet(torecharge.getItemId())){
|
||||
torecharge.setQuantity(mii.getSlotMax(c, torecharge.getItemId()));
|
||||
c.getPlayer().forceUpdateItem(torecharge);
|
||||
} else if (ItemConstants.isConsumable(torecharge.getItemId())){
|
||||
torecharge.setQuantity(mii.getSlotMax(c, torecharge.getItemId()));
|
||||
c.getPlayer().forceUpdateItem(torecharge);
|
||||
}
|
||||
}
|
||||
player.dropMessage(5, "USE Recharged.");
|
||||
break;
|
||||
|
||||
case "whereami":
|
||||
player.yellowMessage("Map ID: " + player.getMap().getId());
|
||||
player.yellowMessage("Players on this map:");
|
||||
@@ -1653,7 +1653,7 @@ public class Commands {
|
||||
}
|
||||
break;
|
||||
|
||||
case "fly":
|
||||
case "fly": // fly option will become available for any character of that account
|
||||
if (sub.length < 2) {
|
||||
player.yellowMessage("Syntax: !fly <on/off>");
|
||||
break;
|
||||
@@ -2776,7 +2776,7 @@ public class Commands {
|
||||
player.getMap().removePlayer(player);//LOL FORGOT THIS ><
|
||||
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
|
||||
player.setWorld(worldb);
|
||||
player.saveToDB();//To set the new world :O (true because else 2 player instances are created, one in both worlds)
|
||||
player.saveCharToDB();//To set the new world :O (true because else 2 player instances are created, one in both worlds)
|
||||
c.announce(MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1])));
|
||||
} catch (UnknownHostException | NumberFormatException ex) {
|
||||
ex.printStackTrace();
|
||||
@@ -2791,7 +2791,7 @@ public class Commands {
|
||||
case "saveall":
|
||||
for (World world : Server.getInstance().getWorlds()) {
|
||||
for (MapleCharacter chr : world.getPlayerStorage().getAllCharacters()) {
|
||||
chr.saveToDB();
|
||||
chr.saveCharToDB();
|
||||
}
|
||||
}
|
||||
String message = player.getName() + " used !saveall.";
|
||||
|
||||
@@ -86,7 +86,7 @@ public abstract class CharacterFactory {
|
||||
}
|
||||
c.announce(MaplePacketCreator.addNewCharEntry(newchar));
|
||||
|
||||
Server.getInstance().createCharacterid(newchar.getAccountID(), newchar.getId(), newchar.getWorld());
|
||||
Server.getInstance().createCharacterEntry(newchar);
|
||||
Server.getInstance().broadcastGMMessage(c.getWorld(), MaplePacketCreator.sendYellowTip("[NEW CHAR]: " + c.getAccountName() + " has created a new character with IGN " + name));
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -42,8 +42,8 @@ public enum ItemFactory {
|
||||
INVENTORY(1, false),
|
||||
STORAGE(2, true),
|
||||
CASH_EXPLORER(3, true),
|
||||
CASH_CYGNUS(4, false),
|
||||
CASH_ARAN(5, false),
|
||||
CASH_CYGNUS(4, true),
|
||||
CASH_ARAN(5, true),
|
||||
MERCHANT(6, false);
|
||||
private final int value;
|
||||
private final boolean account;
|
||||
@@ -72,30 +72,7 @@ public enum ItemFactory {
|
||||
else saveItemsMerchant(items, bundlesList, id, con);
|
||||
}
|
||||
|
||||
private List<Pair<Item, MapleInventoryType>> loadItemsCommon(int id, boolean login) throws SQLException {
|
||||
List<Pair<Item, MapleInventoryType>> items = new ArrayList<>();
|
||||
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try {
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("SELECT * FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
|
||||
query.append(account ? "accountid" : "characterid").append("` = ?");
|
||||
|
||||
if (login) {
|
||||
query.append(" AND `inventorytype` = ").append(MapleInventoryType.EQUIPPED.getType());
|
||||
}
|
||||
|
||||
ps = con.prepareStatement(query.toString());
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
MapleInventoryType mit = MapleInventoryType.getByType(rs.getByte("inventorytype"));
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
private static Equip loadEquipFromResultSet(ResultSet rs) throws SQLException {
|
||||
Equip equip = new Equip(rs.getInt("itemid"), (short) rs.getInt("position"));
|
||||
equip.setOwner(rs.getString("owner"));
|
||||
equip.setQuantity((short) rs.getInt("quantity"));
|
||||
@@ -123,7 +100,65 @@ public enum ItemFactory {
|
||||
equip.setExpiration(rs.getLong("expiration"));
|
||||
equip.setGiftFrom(rs.getString("giftFrom"));
|
||||
equip.setRingId(rs.getInt("ringid"));
|
||||
items.add(new Pair<Item, MapleInventoryType>(equip, mit));
|
||||
|
||||
return equip;
|
||||
}
|
||||
|
||||
public static List<Pair<Item, Integer>> loadEquippedItems(int id, boolean isAccount, boolean login) throws SQLException {
|
||||
List<Pair<Item, Integer>> items = new ArrayList<>();
|
||||
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("SELECT * FROM ");
|
||||
query.append("(SELECT id, accountid FROM characters) AS accountterm ");
|
||||
query.append("RIGHT JOIN ");
|
||||
query.append("(SELECT * FROM (`inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`))) AS equipterm");
|
||||
query.append(" ON accountterm.id=equipterm.characterid ");
|
||||
query.append("WHERE accountterm.`");
|
||||
query.append(isAccount ? "accountid" : "characterid");
|
||||
query.append("` = ?");
|
||||
query.append(login ? " AND `inventorytype` = " + MapleInventoryType.EQUIPPED.getType() : "");
|
||||
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
try (PreparedStatement ps = con.prepareStatement(query.toString())) {
|
||||
ps.setInt(1, id);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
Integer cid = rs.getInt("characterid");
|
||||
items.add(new Pair<Item, Integer>(loadEquipFromResultSet(rs), cid));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
private List<Pair<Item, MapleInventoryType>> loadItemsCommon(int id, boolean login) throws SQLException {
|
||||
List<Pair<Item, MapleInventoryType>> items = new ArrayList<>();
|
||||
|
||||
PreparedStatement ps = null;
|
||||
ResultSet rs = null;
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try {
|
||||
StringBuilder query = new StringBuilder();
|
||||
query.append("SELECT * FROM `inventoryitems` LEFT JOIN `inventoryequipment` USING(`inventoryitemid`) WHERE `type` = ? AND `");
|
||||
query.append(account ? "accountid" : "characterid").append("` = ?");
|
||||
|
||||
if (login) {
|
||||
query.append(" AND `inventorytype` = ").append(MapleInventoryType.EQUIPPED.getType());
|
||||
}
|
||||
|
||||
ps = con.prepareStatement(query.toString());
|
||||
ps.setInt(1, value);
|
||||
ps.setInt(2, id);
|
||||
rs = ps.executeQuery();
|
||||
|
||||
while (rs.next()) {
|
||||
MapleInventoryType mit = MapleInventoryType.getByType(rs.getByte("inventorytype"));
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
items.add(new Pair<Item, MapleInventoryType>(loadEquipFromResultSet(rs), mit));
|
||||
} else {
|
||||
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), rs.getInt("petid"));
|
||||
item.setOwner(rs.getString("owner"));
|
||||
@@ -277,34 +312,7 @@ public enum ItemFactory {
|
||||
MapleInventoryType mit = MapleInventoryType.getByType(rs.getByte("inventorytype"));
|
||||
|
||||
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
|
||||
Equip equip = new Equip(rs.getInt("itemid"), (short) rs.getInt("position"));
|
||||
equip.setOwner(rs.getString("owner"));
|
||||
equip.setQuantity((short) rs.getInt("quantity"));
|
||||
equip.setAcc((short) rs.getInt("acc"));
|
||||
equip.setAvoid((short) rs.getInt("avoid"));
|
||||
equip.setDex((short) rs.getInt("dex"));
|
||||
equip.setHands((short) rs.getInt("hands"));
|
||||
equip.setHp((short) rs.getInt("hp"));
|
||||
equip.setInt((short) rs.getInt("int"));
|
||||
equip.setJump((short) rs.getInt("jump"));
|
||||
equip.setVicious((short) rs.getInt("vicious"));
|
||||
equip.setFlag((byte) rs.getInt("flag"));
|
||||
equip.setLuk((short) rs.getInt("luk"));
|
||||
equip.setMatk((short) rs.getInt("matk"));
|
||||
equip.setMdef((short) rs.getInt("mdef"));
|
||||
equip.setMp((short) rs.getInt("mp"));
|
||||
equip.setSpeed((short) rs.getInt("speed"));
|
||||
equip.setStr((short) rs.getInt("str"));
|
||||
equip.setWatk((short) rs.getInt("watk"));
|
||||
equip.setWdef((short) rs.getInt("wdef"));
|
||||
equip.setUpgradeSlots((byte) rs.getInt("upgradeslots"));
|
||||
equip.setLevel((byte) rs.getByte("level"));
|
||||
equip.setItemExp(rs.getInt("itemexp"));
|
||||
equip.setItemLevel(rs.getByte("itemlevel"));
|
||||
equip.setExpiration(rs.getLong("expiration"));
|
||||
equip.setGiftFrom(rs.getString("giftFrom"));
|
||||
equip.setRingId(rs.getInt("ringid"));
|
||||
items.add(new Pair<Item, MapleInventoryType>(equip, mit));
|
||||
items.add(new Pair<Item, MapleInventoryType>(loadEquipFromResultSet(rs), mit));
|
||||
} else {
|
||||
if(bundles > 0) {
|
||||
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), rs.getInt("petid"));
|
||||
|
||||
@@ -241,30 +241,15 @@ public class NewYearCardRecord {
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCharacterWorld(int receiverid) {
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT world FROM characters WHERE id = ?")) {
|
||||
ps.setInt(1, receiverid);
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if (rs.next()) {
|
||||
return rs.getInt("world");
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch(SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
public void startNewYearCardTask() {
|
||||
if(sendTask != null) return;
|
||||
|
||||
sendTask = TimerManager.getInstance().register(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
int world = getCharacterWorld(receiverId);
|
||||
Server server = Server.getInstance();
|
||||
|
||||
int world = server.getCharacterWorld(receiverId);
|
||||
if(world == -1) {
|
||||
sendTask.cancel(false);
|
||||
sendTask = null;
|
||||
@@ -272,7 +257,7 @@ public class NewYearCardRecord {
|
||||
return;
|
||||
}
|
||||
|
||||
MapleCharacter target = Server.getInstance().getWorld(world).getPlayerStorage().getCharacterById(receiverId);
|
||||
MapleCharacter target = server.getWorld(world).getPlayerStorage().getCharacterById(receiverId);
|
||||
if(target != null && target.isLoggedinWorld()) {
|
||||
target.announce(MaplePacketCreator.onNewYearCardRes(target, NewYearCardRecord.this, 0xC, 0));
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ public class ServerConstants {
|
||||
public static final long PURGING_INTERVAL = 5 * 60 * 1000;
|
||||
public static final long RANKING_INTERVAL = 60 * 60 * 1000; //60 minutes, 3600000.
|
||||
public static final long COUPON_INTERVAL = 60 * 60 * 1000; //60 minutes, 3600000.
|
||||
public static final long UPDATE_INTERVAL = 777;
|
||||
|
||||
public static final boolean ENABLE_PIC = false; //Pick true/false to enable or disable Pic. Delete character needs this feature ENABLED.
|
||||
public static final boolean ENABLE_PIN = false; //Pick true/false to enable or disable Pin.
|
||||
@@ -66,7 +67,7 @@ public class ServerConstants {
|
||||
public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs.
|
||||
public static final boolean USE_ENFORCE_OWL_SUGGESTIONS = false;//Forces the Owl of Minerva to always display the defined item array on GameConstants.OWL_DATA instead of those featured by the players.
|
||||
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
|
||||
public static final boolean USE_ENFORCE_MDOOR_POSITION = true; //Forces mystic door to be spawned near spawnpoints. (since things bugs out other way, and this helps players to locate the door faster)
|
||||
public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints.
|
||||
public static final boolean USE_ERASE_PERMIT_ON_OPENSHOP = true;//Forces "shop permit" item to be consumed when player deploy his/her player shop.
|
||||
public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped.
|
||||
public static final boolean USE_ERASE_PET_ON_EXPIRATION = false;//Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll.
|
||||
|
||||
@@ -24,6 +24,7 @@ package net;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import net.opcodes.RecvOpcode;
|
||||
import net.server.channel.handlers.*;
|
||||
import net.server.handlers.CustomPacketHandler;
|
||||
import net.server.handlers.KeepAliveHandler;
|
||||
@@ -38,16 +39,16 @@ import net.server.handlers.login.CreateCharHandler;
|
||||
import net.server.handlers.login.DeleteCharHandler;
|
||||
import net.server.handlers.login.GuestLoginHandler;
|
||||
import net.server.handlers.login.LoginPasswordHandler;
|
||||
import net.server.handlers.login.PickCharHandler;
|
||||
import net.server.handlers.login.RegisterPicHandler;
|
||||
import net.server.handlers.login.RegisterPinHandler;
|
||||
import net.server.handlers.login.RelogRequestHandler;
|
||||
import net.server.handlers.login.ServerStatusRequestHandler;
|
||||
import net.server.handlers.login.ServerlistRequestHandler;
|
||||
import net.server.handlers.login.SetGenderHandler;
|
||||
import net.server.handlers.login.ViewAllCharHandler;
|
||||
import net.server.handlers.login.ViewAllCharRegisterPicHandler;
|
||||
import net.server.handlers.login.ViewAllCharSelectedHandler;
|
||||
import net.server.handlers.login.ViewAllCharSelectedWithPicHandler;
|
||||
import net.server.handlers.login.ViewAllPicRegisterHandler;
|
||||
import net.server.handlers.login.ViewCharHandler;
|
||||
|
||||
public final class PacketProcessor {
|
||||
|
||||
@@ -114,15 +115,15 @@ public final class PacketProcessor {
|
||||
registerHandler(RecvOpcode.CHECK_CHAR_NAME, new CheckCharNameHandler());
|
||||
registerHandler(RecvOpcode.CREATE_CHAR, new CreateCharHandler());
|
||||
registerHandler(RecvOpcode.DELETE_CHAR, new DeleteCharHandler());
|
||||
registerHandler(RecvOpcode.VIEW_ALL_CHAR, new ViewCharHandler());
|
||||
registerHandler(RecvOpcode.PICK_ALL_CHAR, new PickCharHandler());
|
||||
registerHandler(RecvOpcode.VIEW_ALL_CHAR, new ViewAllCharHandler());
|
||||
registerHandler(RecvOpcode.PICK_ALL_CHAR, new ViewAllCharSelectedHandler());
|
||||
registerHandler(RecvOpcode.REGISTER_PIN, new RegisterPinHandler());
|
||||
registerHandler(RecvOpcode.GUEST_LOGIN, new GuestLoginHandler());
|
||||
registerHandler(RecvOpcode.REGISTER_PIC, new RegisterPicHandler());
|
||||
registerHandler(RecvOpcode.CHAR_SELECT_WITH_PIC, new CharSelectedWithPicHandler());
|
||||
registerHandler(RecvOpcode.SET_GENDER, new SetGenderHandler());
|
||||
registerHandler(RecvOpcode.VIEW_ALL_WITH_PIC, new ViewAllCharSelectedWithPicHandler());
|
||||
registerHandler(RecvOpcode.VIEW_ALL_PIC_REGISTER, new ViewAllPicRegisterHandler());
|
||||
registerHandler(RecvOpcode.VIEW_ALL_PIC_REGISTER, new ViewAllCharRegisterPicHandler());
|
||||
} else {
|
||||
//CHANNEL HANDLERS
|
||||
registerHandler(RecvOpcode.NAME_TRANSFER, new TransferNameHandler());
|
||||
@@ -237,7 +238,8 @@ public final class PacketProcessor {
|
||||
registerHandler(RecvOpcode.ACCEPT_FAMILY, new AcceptFamilyHandler());
|
||||
registerHandler(RecvOpcode.DUEY_ACTION, new DueyHandler());
|
||||
registerHandler(RecvOpcode.USE_DEATHITEM, new UseDeathItemHandler());
|
||||
registerHandler(RecvOpcode.PLAYER_UPDATE, new PlayerUpdateHandler());
|
||||
//registerHandler(RecvOpcode.PLAYER_UPDATE, new PlayerUpdateHandler()); unused
|
||||
registerHandler(RecvOpcode.PLAYER_MAP_TRANSFER, new PlayerMapTransitionHandler());
|
||||
registerHandler(RecvOpcode.USE_MAPLELIFE, new UseMapleLifeHandler());
|
||||
registerHandler(RecvOpcode.USE_CATCH_ITEM, new UseCatchItemHandler());
|
||||
registerHandler(RecvOpcode.MOB_DAMAGE_MOB_FRIENDLY, new MobDamageMobFriendlyHandler());
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net;
|
||||
package net.opcodes;
|
||||
|
||||
public enum RecvOpcode {
|
||||
CUSTOM_PACKET(0x3713),//13 37 lol
|
||||
@@ -174,7 +174,7 @@ public enum RecvOpcode {
|
||||
ITEM_PICKUP(0xCA),
|
||||
DAMAGE_REACTOR(0xCD),
|
||||
TOUCHING_REACTOR(0xCE),
|
||||
TEMP_SKILL(0xCF),
|
||||
PLAYER_MAP_TRANSFER(0xCF),
|
||||
MAPLETV(0xFFFE),//Don't know
|
||||
SNOWBALL(0xD3),
|
||||
LEFT_KNOCKBACK(0xD4),
|
||||
@@ -19,7 +19,7 @@
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net;
|
||||
package net.opcodes;
|
||||
|
||||
public enum SendOpcode {
|
||||
|
||||
+233
-108
@@ -21,9 +21,6 @@
|
||||
*/
|
||||
package net.server;
|
||||
|
||||
import net.server.worker.CharacterDiseaseWorker;
|
||||
import net.server.worker.CouponWorker;
|
||||
import net.server.worker.RankingWorker;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
@@ -43,14 +40,22 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import tools.locks.MonitoredReentrantLock;
|
||||
import tools.locks.MonitoredReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
|
||||
import net.MapleServerHandler;
|
||||
import net.mina.MapleCodecFactory;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.guild.MapleAlliance;
|
||||
import net.server.guild.MapleGuild;
|
||||
import net.server.guild.MapleGuildCharacter;
|
||||
import net.server.worker.CharacterDiseaseWorker;
|
||||
import net.server.worker.CouponWorker;
|
||||
import net.server.worker.RankingWorker;
|
||||
import net.server.world.World;
|
||||
|
||||
import org.apache.mina.core.buffer.IoBuffer;
|
||||
@@ -64,6 +69,8 @@ import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||
import client.MapleClient;
|
||||
import client.MapleCharacter;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ItemFactory;
|
||||
import client.newyear.NewYearCardRecord;
|
||||
import constants.ItemConstants;
|
||||
import constants.GameConstants;
|
||||
@@ -96,14 +103,18 @@ public class Server {
|
||||
private final Map<Integer, MapleGuild> guilds = new HashMap<>(100);
|
||||
private final Map<MapleClient, Long> inLoginState = new HashMap<>(100);
|
||||
private final Lock srvLock = new MonitoredReentrantLock(MonitoredLockType.SERVER);
|
||||
private final Lock lgnLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_LOGIN);
|
||||
private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_DISEASES);
|
||||
private final ReentrantReadWriteLock lgnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_LOGIN, true);
|
||||
private final ReadLock lgnRLock = lgnLock.readLock();
|
||||
private final WriteLock lgnWLock = lgnLock.writeLock();
|
||||
private final PlayerBuffStorage buffStorage = new PlayerBuffStorage();
|
||||
private final Map<Integer, MapleAlliance> alliances = new HashMap<>(100);
|
||||
private final Map<Integer, NewYearCardRecord> newyears = new HashMap<>();
|
||||
private final List<MapleClient> processDiseaseAnnouncePlayers = new LinkedList<>();
|
||||
private final List<MapleClient> registeredDiseaseAnnouncePlayers = new LinkedList<>();
|
||||
|
||||
private final AtomicLong currentTime = new AtomicLong(0);
|
||||
|
||||
private boolean availableDeveloperRoom = false;
|
||||
private boolean online = false;
|
||||
public static long uptime = System.currentTimeMillis();
|
||||
@@ -115,6 +126,20 @@ public class Server {
|
||||
return instance;
|
||||
}
|
||||
|
||||
public void updateCurrentTime() {
|
||||
currentTime.addAndGet(ServerConstants.UPDATE_INTERVAL);
|
||||
}
|
||||
|
||||
public long forceUpdateCurrentTime() {
|
||||
long timeNow = System.currentTimeMillis();
|
||||
currentTime.set(timeNow);
|
||||
return timeNow;
|
||||
}
|
||||
|
||||
public long getCurrentTime() { // returns a slightly delayed time value
|
||||
return currentTime.get();
|
||||
}
|
||||
|
||||
public boolean isOnline() {
|
||||
return online;
|
||||
}
|
||||
@@ -372,7 +397,7 @@ public class Server {
|
||||
disconnectIdlesOnLoginTask();
|
||||
|
||||
long timeLeft = getTimeLeftForNextHour();
|
||||
tMan.register(new CharacterDiseaseWorker(), 777, 777);
|
||||
tMan.register(new CharacterDiseaseWorker(), ServerConstants.UPDATE_INTERVAL, ServerConstants.UPDATE_INTERVAL);
|
||||
tMan.register(new CouponWorker(), ServerConstants.COUPON_INTERVAL, timeLeft);
|
||||
tMan.register(new RankingWorker(), ServerConstants.RANKING_INTERVAL, timeLeft);
|
||||
|
||||
@@ -823,122 +848,222 @@ public class Server {
|
||||
return worlds;
|
||||
}
|
||||
|
||||
private static void loadCharacteridsFromDb(Integer accountid, Set<Integer> accChars) {
|
||||
try {
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT id FROM characters WHERE accountid = ?")) {
|
||||
ps.setInt(1, accountid);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while(rs.next()) {
|
||||
accChars.add(rs.getInt("id"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean haveCharacterid(Integer accountid, Integer chrid) {
|
||||
lgnLock.lock();
|
||||
try {
|
||||
Set<Integer> accChars = accountChars.get(accountid);
|
||||
if(accChars == null) {
|
||||
accChars = new HashSet<>(5);
|
||||
loadCharacteridsFromDb(accountid, accChars);
|
||||
|
||||
accountChars.put(accountid, accChars);
|
||||
}
|
||||
|
||||
return accChars.contains(chrid);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void createCharacterid(Integer accountid, Integer chrid, Integer world) {
|
||||
lgnLock.lock();
|
||||
try {
|
||||
Set<Integer> accChars = accountChars.get(accountid);
|
||||
if(accChars == null) {
|
||||
accChars = new HashSet<>(5);
|
||||
accountChars.put(accountid, accChars);
|
||||
}
|
||||
|
||||
accChars.add(chrid);
|
||||
worldChars.put(chrid, world);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteCharacterid(Integer accountid, Integer chrid) {
|
||||
lgnLock.lock();
|
||||
try {
|
||||
Set<Integer> accChars = accountChars.get(accountid);
|
||||
if(accChars != null) {
|
||||
accChars.remove(chrid);
|
||||
}
|
||||
|
||||
worldChars.remove(chrid);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static int getCharacterWorldFromDB(int chrid) {
|
||||
int world = -1;
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT world FROM characters WHERE id = ?")) {
|
||||
ps.setInt(1, chrid);
|
||||
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
if(rs.next()) {
|
||||
world = rs.getInt("world");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
return world;
|
||||
}
|
||||
|
||||
public int getCharacterWorld(Integer chrid) {
|
||||
lgnLock.lock();
|
||||
lgnRLock.lock();
|
||||
try {
|
||||
Integer worldid = worldChars.get(chrid);
|
||||
|
||||
if(worldid == null) {
|
||||
worldid = getCharacterWorldFromDB(chrid);
|
||||
worldChars.put(chrid, worldid);
|
||||
return worldid != null ? worldid : -1;
|
||||
} finally {
|
||||
lgnRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
return worldid;
|
||||
public boolean haveCharacterEntry(Integer accountid, Integer chrid) {
|
||||
lgnRLock.lock();
|
||||
try {
|
||||
Set<Integer> accChars = accountChars.get(accountid);
|
||||
return accChars.contains(chrid);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
lgnRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private Set<Integer> getAccountCharacterEntries(Integer accountid) {
|
||||
lgnRLock.lock();
|
||||
try {
|
||||
return new HashSet<>(accountChars.get(accountid));
|
||||
} finally {
|
||||
lgnRLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateCharacterEntry(MapleCharacter chr) {
|
||||
MapleCharacter chrView = chr.generateCharacterEntry();
|
||||
World wserv = worlds.get(chrView.getWorld());
|
||||
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void createCharacterEntry(MapleCharacter chr) {
|
||||
Integer accountid = chr.getAccountID(), chrid = chr.getId(), world = chr.getWorld();
|
||||
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
Set<Integer> accChars = accountChars.get(accountid);
|
||||
accChars.add(chrid);
|
||||
|
||||
worldChars.put(chrid, world);
|
||||
|
||||
MapleCharacter chrView = chr.generateCharacterEntry();
|
||||
World wserv = worlds.get(chrView.getWorld());
|
||||
wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void deleteCharacterEntry(Integer accountid, Integer chrid) {
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
Set<Integer> accChars = accountChars.get(accountid);
|
||||
accChars.remove(chrid);
|
||||
|
||||
Integer world = worldChars.remove(chrid);
|
||||
if(world != null) {
|
||||
World wserv = worlds.get(world);
|
||||
wserv.unregisterAccountCharacterView(accountid, chrid);
|
||||
}
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void transferWorldCharacterEntry(MapleCharacter chr, Integer toWorld) { // used before setting the new worldid on the character object
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
Integer chrid = chr.getId(), accountid = chr.getAccountID(), world = worldChars.get(chr.getId());
|
||||
if(world != null) {
|
||||
World wserv = worlds.get(world);
|
||||
wserv.unregisterAccountCharacterView(accountid, chrid);
|
||||
}
|
||||
|
||||
worldChars.put(chrid, toWorld);
|
||||
|
||||
MapleCharacter chrView = chr.generateCharacterEntry();
|
||||
World wserv = worlds.get(toWorld);
|
||||
wserv.registerAccountCharacterView(chrView.getAccountID(), chrView);
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
public void deleteAccount(Integer accountid) { is this even a thing?
|
||||
lgnLock.lock();
|
||||
public void deleteAccountEntry(Integer accountid) { is this even a thing?
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
accountChars.remove(accountid);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
private static List<List<MapleCharacter>> loadAccountCharactersViewFromDb(MapleClient c, int wlen) {
|
||||
List<List<MapleCharacter>> wchars = new ArrayList<>(wlen);
|
||||
for(int i = 0; i < wlen; i++) wchars.add(i, new LinkedList<MapleCharacter>());
|
||||
|
||||
List<MapleCharacter> chars = new LinkedList<>();
|
||||
int curWorld = 0;
|
||||
try {
|
||||
List<Pair<Item, Integer>> accEquips = ItemFactory.loadEquippedItems(c.getAccID(), true, true);
|
||||
Map<Integer, List<Item>> accPlayerEquips = new HashMap<>();
|
||||
|
||||
for(Pair<Item, Integer> ae : accEquips) {
|
||||
List<Item> playerEquips = accPlayerEquips.get(ae.getRight());
|
||||
if(playerEquips == null) {
|
||||
playerEquips = new LinkedList<>();
|
||||
accPlayerEquips.put(ae.getRight(), playerEquips);
|
||||
}
|
||||
|
||||
playerEquips.add(ae.getLeft());
|
||||
}
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT * FROM characters WHERE accountid = ? ORDER BY world, id")) {
|
||||
ps.setInt(1, c.getAccID());
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
int cworld = rs.getByte("world");
|
||||
if(cworld >= wlen) break;
|
||||
|
||||
if(cworld > curWorld) {
|
||||
wchars.add(curWorld, chars);
|
||||
|
||||
curWorld = cworld;
|
||||
chars = new LinkedList<>();
|
||||
}
|
||||
|
||||
Integer cid = rs.getInt("id");
|
||||
chars.add(MapleCharacter.loadCharacterEntryFromDB(rs, accPlayerEquips.get(cid)));
|
||||
}
|
||||
}
|
||||
}
|
||||
con.close();
|
||||
|
||||
wchars.add(curWorld, chars);
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
|
||||
return wchars;
|
||||
}
|
||||
|
||||
public void loadAccountCharactersView(MapleClient c) {
|
||||
int accId = c.getAccID();
|
||||
int gmLevel = 0;
|
||||
boolean firstAccountLogin;
|
||||
|
||||
lgnRLock.lock();
|
||||
try {
|
||||
firstAccountLogin = !accountChars.containsKey(accId);
|
||||
} finally {
|
||||
lgnRLock.unlock();
|
||||
}
|
||||
|
||||
if(!firstAccountLogin) {
|
||||
Set<Integer> accWorlds = new HashSet<>();
|
||||
|
||||
lgnRLock.lock();
|
||||
try {
|
||||
for(Integer chrid : getAccountCharacterEntries(accId)) {
|
||||
accWorlds.add(worldChars.get(chrid));
|
||||
}
|
||||
} finally {
|
||||
lgnRLock.unlock();
|
||||
}
|
||||
|
||||
for(Integer aw : accWorlds) {
|
||||
for(MapleCharacter chr : worlds.get(aw).getAllCharactersView()) {
|
||||
if(gmLevel < chr.gmLevel()) gmLevel = chr.gmLevel();
|
||||
}
|
||||
}
|
||||
|
||||
c.setGMLevel(gmLevel);
|
||||
return;
|
||||
}
|
||||
|
||||
List<List<MapleCharacter>> accChars = loadAccountCharactersViewFromDb(c, worlds.size());
|
||||
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
Set<Integer> chars = new HashSet<>(5);
|
||||
for(int wid = 0; wid < worlds.size(); wid++) {
|
||||
World w = worlds.get(wid);
|
||||
List<MapleCharacter> wchars = accChars.get(wid);
|
||||
w.loadAccountCharactersView(accId, wchars);
|
||||
|
||||
for(MapleCharacter chr : wchars) {
|
||||
int cid = chr.getId();
|
||||
if(gmLevel < chr.gmLevel()) gmLevel = chr.gmLevel();
|
||||
|
||||
chars.add(cid);
|
||||
worldChars.put(cid, wid);
|
||||
}
|
||||
}
|
||||
|
||||
accountChars.put(accId, chars);
|
||||
} finally {
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
|
||||
c.setGMLevel(gmLevel);
|
||||
}
|
||||
|
||||
private static String getRemoteIp(InetSocketAddress isa) {
|
||||
return isa.getAddress().getHostAddress();
|
||||
}
|
||||
@@ -946,23 +1071,23 @@ public class Server {
|
||||
public void setCharacteridInTransition(InetSocketAddress isa, int charId) {
|
||||
String remoteIp = getRemoteIp(isa);
|
||||
|
||||
lgnLock.lock();
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
transitioningChars.put(remoteIp, charId);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean validateCharacteridInTransition(InetSocketAddress isa, int charId) {
|
||||
String remoteIp = getRemoteIp(isa);
|
||||
|
||||
lgnLock.lock();
|
||||
lgnWLock.lock();
|
||||
try {
|
||||
Integer cid = transitioningChars.remove(remoteIp);
|
||||
return cid != null && cid.equals(charId);
|
||||
} finally {
|
||||
lgnLock.unlock();
|
||||
lgnWLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -132,7 +132,7 @@ public final class AdminCommandHandler extends AbstractMaplePacketHandler {
|
||||
MapleMonster monster = (MapleMonster) monsterx.get(x);
|
||||
if (monster.getId() == mobToKill) {
|
||||
c.getPlayer().getMap().killMonster(monster, c.getPlayer(), true);
|
||||
monster.giveExpToCharacter(c.getPlayer(), monster.getExp(), true, 1);
|
||||
//monster.giveExpToCharacter(c.getPlayer(), monster.getExp(), true, 1); already being done
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -24,7 +24,7 @@ package net.server.channel.handlers;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.SendOpcode;
|
||||
import net.opcodes.SendOpcode;
|
||||
import net.server.Server;
|
||||
import net.server.guild.MapleGuild;
|
||||
import net.server.guild.MapleGuildCharacter;
|
||||
|
||||
@@ -23,17 +23,19 @@ package net.server.channel.handlers;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.UnknownHostException;
|
||||
import java.util.Calendar;
|
||||
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventoryType;
|
||||
import client.inventory.manipulator.MapleInventoryManipulator;
|
||||
import server.MaplePortal;
|
||||
import server.MapleTrade;
|
||||
import server.maps.MapleMap;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.inventory.MapleInventoryType;
|
||||
|
||||
public final class ChangeMapHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@@ -42,6 +44,10 @@ public final class ChangeMapHandler extends AbstractMaplePacketHandler {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
|
||||
if (chr.isChangingMaps() || chr.isBanned()) {
|
||||
if(chr.isChangingMaps()) {
|
||||
FilePrinter.printError(FilePrinter.PORTAL_STUCK + chr.getName() + ".txt", "Player " + chr.getName() + " got stuck when changing maps. Timestamp: " + Calendar.getInstance().getTime().toString() + " Last visited mapids: " + chr.getLastVisitedMapids());
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
@@ -138,12 +144,13 @@ public final class ChangeMapHandler extends AbstractMaplePacketHandler {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
if (chr.getMapId() == 109040004) {
|
||||
chr.getFitness().resetTimes();
|
||||
}
|
||||
if (chr.getMapId() == 109030003 || chr.getMapId() == 109030103) {
|
||||
} else if (chr.getMapId() == 109030003 || chr.getMapId() == 109030103) {
|
||||
chr.getOla().resetTimes();
|
||||
}
|
||||
|
||||
if (portal != null) {
|
||||
if(portal.getPosition().distanceSq(chr.getPosition()) > 400000) {
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
|
||||
@@ -21,10 +21,14 @@
|
||||
*/
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import java.util.Calendar;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import server.maps.MapleDoorObject;
|
||||
import server.maps.MapleMapObject;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
@@ -35,15 +39,29 @@ public final class DoorHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
int ownerid = slea.readInt();
|
||||
boolean mode = (slea.readByte() == 0); // specifies if backwarp or not, 1 town to target, 0 target to town
|
||||
for (MapleMapObject obj : c.getPlayer().getMap().getMapObjects()) {
|
||||
slea.readByte(); // specifies if backwarp or not, 1 town to target, 0 target to town
|
||||
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
if (chr.isChangingMaps() || chr.isBanned()) {
|
||||
if(chr.isChangingMaps()) {
|
||||
FilePrinter.printError(FilePrinter.PORTAL_STUCK + chr.getName() + ".txt", "Player " + chr.getName() + " got stuck when changing maps (using Mystic Door). Timestamp: " + Calendar.getInstance().getTime().toString() + " Last visited mapids: " + chr.getLastVisitedMapids());
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
return;
|
||||
}
|
||||
|
||||
for (MapleMapObject obj : chr.getMap().getMapObjects()) {
|
||||
if (obj instanceof MapleDoorObject) {
|
||||
MapleDoorObject door = (MapleDoorObject) obj;
|
||||
if (door.getOwnerId() == ownerid) {
|
||||
door.warp(c.getPlayer(), mode);
|
||||
door.warp(chr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.announce(MaplePacketCreator.blockedMessage(6));
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler {
|
||||
c.getChannelServer().removePlayer(mc);
|
||||
mc.getMap().removePlayer(mc);
|
||||
mc.getCashShop().open(true);
|
||||
mc.saveToDB();
|
||||
mc.saveCharToDB();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
|
||||
chr.forfeitExpirableQuests();
|
||||
chr.cancelQuestExpirationTask();
|
||||
|
||||
chr.saveToDB();
|
||||
chr.saveCharToDB();
|
||||
chr.getMap().removePlayer(c.getPlayer());
|
||||
try {
|
||||
c.announce(MaplePacketCreator.openCashShop(c, true));
|
||||
|
||||
@@ -25,7 +25,7 @@ import constants.ServerConstants;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.SendOpcode;
|
||||
import net.opcodes.SendOpcode;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import tools.data.output.MaplePacketLittleEndianWriter;
|
||||
|
||||
|
||||
@@ -26,12 +26,14 @@ import client.MapleClient;
|
||||
import java.awt.Point;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.server.Server;
|
||||
import server.life.MapleMonster;
|
||||
import server.life.MapleMonsterInformationProvider;
|
||||
//import server.life.MobAttackInfo;
|
||||
//import server.life.MobAttackInfoFactory;
|
||||
import server.life.MobSkill;
|
||||
import server.life.MobSkillFactory;
|
||||
import server.maps.MapleMap;
|
||||
import server.maps.MapleMapObject;
|
||||
import server.maps.MapleMapObjectType;
|
||||
import server.movement.LifeMovementFragment;
|
||||
@@ -48,9 +50,12 @@ import tools.data.input.SeekableLittleEndianAccessor;
|
||||
public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleCharacter player = c.getPlayer();
|
||||
MapleMap map = player.getMap();
|
||||
|
||||
int objectid = slea.readInt();
|
||||
short moveid = slea.readShort();
|
||||
MapleMapObject mmo = c.getPlayer().getMap().getMapObject(objectid);
|
||||
MapleMapObject mmo = map.getMapObject(objectid);
|
||||
if (mmo == null || mmo.getType() != MapleMapObjectType.MONSTER) {
|
||||
return;
|
||||
}
|
||||
@@ -96,7 +101,17 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
nextCastSkillLevel = skillToUse.getRight();
|
||||
toUse = MobSkillFactory.getMobSkill(nextCastSkill, nextCastSkillLevel);
|
||||
|
||||
if (isSkill || isAttack) {
|
||||
if (!isSkill && !isAttack) {
|
||||
long curtime = Server.getInstance().getCurrentTime();
|
||||
if(curtime >= monster.getNextBasicSkillTime()) { // dont use the special attack too often, chase the player f3
|
||||
//MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId);
|
||||
monster.setNextBasicSkillTime(curtime);
|
||||
} else {
|
||||
toUse = null; // paliative measure for suspicious mob movement
|
||||
}
|
||||
}
|
||||
|
||||
if (toUse != null) {
|
||||
int percHpLeft = (int) (((float) monster.getHp() / monster.getMaxHp()) * 100);
|
||||
if (nextCastSkill != toUse.getSkillId() || nextCastSkillLevel != toUse.getSkillLevel()) {
|
||||
//toUse.resetAnticipatedSkill();
|
||||
@@ -106,25 +121,13 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
} else if (monster.canUseSkill(toUse)) {
|
||||
int animationTime = MapleMonsterInformationProvider.getInstance().getMobSkillAnimationTime(monster.getId(), rndSkill);
|
||||
if(animationTime > 0) {
|
||||
toUse.applyDelayedEffect(c.getPlayer(), monster, true, banishPlayers, animationTime);
|
||||
toUse.applyDelayedEffect(player, monster, true, banishPlayers, animationTime);
|
||||
} else {
|
||||
toUse.applyEffect(c.getPlayer(), monster, true, banishPlayers);
|
||||
toUse.applyEffect(player, monster, true, banishPlayers);
|
||||
}
|
||||
} else {
|
||||
toUse = null;
|
||||
}
|
||||
} else {
|
||||
toUse = null; // paliative measure for suspicious mob movement
|
||||
|
||||
/*
|
||||
long curtime = System.currentTimeMillis();
|
||||
if(curtime >= monster.getNextBasicSkillTime()) { // dont use the special attack too often, chase the player f3
|
||||
MobAttackInfo mobAttack = MobAttackInfoFactory.getMobAttackInfo(monster, attackId);
|
||||
monster.setNextBasicSkillTime(curtime);
|
||||
} else {
|
||||
toUse = null;
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -133,15 +136,22 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if(toUse == null) {
|
||||
useSkillId = 0;
|
||||
useSkillLevel = 0;
|
||||
rawActivity = -1;
|
||||
pOption = 0;
|
||||
}
|
||||
|
||||
slea.readByte();
|
||||
slea.readInt(); // whatever
|
||||
short start_x = slea.readShort(); // hmm.. startpos?
|
||||
short start_y = slea.readShort(); // hmm...
|
||||
Point startPos = new Point(start_x, start_y);
|
||||
res = parseMovement(slea);
|
||||
if (monster.getController() != c.getPlayer()) {
|
||||
if (monster.isAttackedBy(c.getPlayer())) {
|
||||
monster.switchController(c.getPlayer(), true);
|
||||
if (monster.getController() != player) {
|
||||
if (monster.isAttackedBy(player)) {
|
||||
monster.switchController(player, true);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
@@ -150,18 +160,20 @@ public final class MoveLifeHandler extends AbstractMovementPacketHandler {
|
||||
monster.setControllerKnowsAboutAggro(false);
|
||||
}
|
||||
boolean aggro = monster.isControllerHasAggro();
|
||||
|
||||
if (toUse != null) {
|
||||
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, monster.getMp(), aggro, toUse.getSkillId(), toUse.getSkillLevel()));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.moveMonsterResponse(objectid, moveid, monster.getMp(), aggro));
|
||||
}
|
||||
|
||||
if (aggro) {
|
||||
monster.setControllerKnowsAboutAggro(true);
|
||||
}
|
||||
if (res != null) {
|
||||
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, res), monster.getPosition());
|
||||
map.broadcastMessage(player, MaplePacketCreator.moveMonster(objectid, nextMovementCouldBeSkill, rawActivity, useSkillId, useSkillLevel, pOption, startPos, res), monster.getPosition());
|
||||
updatePosition(res, monster, -1);
|
||||
c.getPlayer().getMap().moveMonster(monster, monster.getPosition());
|
||||
map.moveMonster(monster, monster.getPosition());
|
||||
}
|
||||
|
||||
for (MapleCharacter chr : banishPlayers) {
|
||||
|
||||
@@ -23,7 +23,7 @@ package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.SendOpcode;
|
||||
import net.opcodes.SendOpcode;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import tools.data.output.MaplePacketLittleEndianWriter;
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ronan
|
||||
*/
|
||||
public final class PlayerMapTransitionHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
c.getPlayer().setMapTransitionComplete();
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,5 @@ import tools.data.input.SeekableLittleEndianAccessor;
|
||||
public final class PlayerUpdateHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
c.getPlayer().setMapTransitionComplete();
|
||||
}
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
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
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
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
|
||||
|
||||
@@ -60,6 +60,8 @@ public abstract class BaseScheduler {
|
||||
}
|
||||
|
||||
private void runBaseSchedule() {
|
||||
List<Object> toRemove;
|
||||
|
||||
schedulerLock.lock();
|
||||
try {
|
||||
if(registeredEntries.isEmpty()) {
|
||||
@@ -77,7 +79,7 @@ public abstract class BaseScheduler {
|
||||
idleProcs = 0;
|
||||
|
||||
long timeNow = System.currentTimeMillis();
|
||||
List<Object> toRemove = new LinkedList<>();
|
||||
toRemove = new LinkedList<>();
|
||||
for(Entry<Object, Pair<Runnable, Long>> rmd : registeredEntries.entrySet()) {
|
||||
Pair<Runnable, Long> r = rmd.getValue();
|
||||
|
||||
@@ -90,17 +92,11 @@ public abstract class BaseScheduler {
|
||||
for(Object mse : toRemove) {
|
||||
registeredEntries.remove(mse);
|
||||
}
|
||||
|
||||
dispatchRemovedEntries(toRemove, true);
|
||||
} finally {
|
||||
schedulerLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void dispatchRemovedEntries(List<Object> toRemove, boolean fromUpdate) {
|
||||
for (SchedulerListener listener : listeners.toArray(new SchedulerListener[listeners.size()])) {
|
||||
listener.removedScheduledEntries(toRemove, fromUpdate);
|
||||
}
|
||||
dispatchRemovedEntries(toRemove, true);
|
||||
}
|
||||
|
||||
protected void registerEntry(Object key, Runnable removalAction, long duration) {
|
||||
@@ -122,10 +118,16 @@ public abstract class BaseScheduler {
|
||||
try {
|
||||
Pair<Runnable, Long> rm = registeredEntries.remove(key);
|
||||
if(rm != null) rm.getLeft().run();
|
||||
|
||||
dispatchRemovedEntries(Collections.singletonList(key), false);
|
||||
} finally {
|
||||
schedulerLock.unlock();
|
||||
}
|
||||
|
||||
dispatchRemovedEntries(Collections.singletonList(key), false);
|
||||
}
|
||||
|
||||
private void dispatchRemovedEntries(List<Object> toRemove, boolean fromUpdate) {
|
||||
for (SchedulerListener listener : listeners.toArray(new SchedulerListener[listeners.size()])) {
|
||||
listener.removedScheduledEntries(toRemove, fromUpdate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -62,7 +62,9 @@ public class MobAnimationScheduler extends BaseScheduler {
|
||||
public boolean registerAnimationMode(Integer mobHash, long animationTime) {
|
||||
animationLock.lock();
|
||||
try {
|
||||
if(onAnimationMobs.contains(mobHash)) return false;
|
||||
if(onAnimationMobs.contains(mobHash)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
registerEntry(mobHash, r, animationTime);
|
||||
onAnimationMobs.add(mobHash);
|
||||
|
||||
@@ -30,7 +30,6 @@ public final class AfterLoginHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
|
||||
byte c2 = slea.readByte();
|
||||
byte c3 = 5;
|
||||
if (slea.available() > 0) {
|
||||
|
||||
@@ -46,7 +46,7 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterid(c.getAccID(), charId)) {
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,7 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterid(c.getAccID(), charId)) {
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -113,6 +113,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
|
||||
}
|
||||
|
||||
private static void login(MapleClient c){
|
||||
Server.getInstance().loadAccountCharactersView(c); // locks the login session until data is recovered from the cache or the DB.
|
||||
c.announce(MaplePacketCreator.getAuthSuccess(c));//why the fk did I do c.getAccountName()?
|
||||
|
||||
Server.getInstance().registerLoginState(c);
|
||||
|
||||
@@ -27,7 +27,7 @@ public final class RegisterPicHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterid(c.getAccID(), charId)) {
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,10 +32,13 @@ public final class ServerStatusRequestHandler extends AbstractMaplePacketHandler
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
byte world = (byte) slea.readShort();//Wuuu? ):
|
||||
byte world = (byte) slea.readShort();
|
||||
World wserv = Server.getInstance().getWorld(world);
|
||||
if(wserv != null) {
|
||||
int status = wserv.getWorldCapacityStatus();
|
||||
|
||||
c.announce(MaplePacketCreator.getServerStatus(status));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.getServerStatus(2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
/*
|
||||
This file is part of the OdinMS Maple Story Server
|
||||
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
|
||||
Matthias Butz <matze@odinms.de>
|
||||
Jan Christian Meyer <vimes@odinms.de>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.handlers.login;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import constants.ServerConstants;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.server.Server;
|
||||
import net.server.world.World;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import tools.Pair;
|
||||
|
||||
public final class ViewAllCharHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
try {
|
||||
List<World> wlist = Server.getInstance().getWorlds();
|
||||
List<Pair<Integer, List<MapleCharacter>>> worldChars = new ArrayList<>(wlist.size() + 1);
|
||||
|
||||
int chrTotal = 0;
|
||||
int accountId = c.getAccID();
|
||||
List<MapleCharacter> lastwchars = null;
|
||||
for(World w : wlist) {
|
||||
List<MapleCharacter> wchars = w.getAccountCharactersView(accountId);
|
||||
|
||||
if(!wchars.isEmpty()) {
|
||||
lastwchars = wchars;
|
||||
|
||||
worldChars.add(new Pair<>(w.getId(), wchars));
|
||||
chrTotal += wchars.size();
|
||||
}
|
||||
}
|
||||
|
||||
if (chrTotal > 9) {
|
||||
int padRight = chrTotal % 3;
|
||||
if (padRight > 0 && lastwchars != null) {
|
||||
MapleCharacter chr = lastwchars.get(lastwchars.size() - 1);
|
||||
|
||||
for(int i = padRight; i < 3; i++) { // filling the remaining slots with the last character loaded
|
||||
chrTotal++;
|
||||
lastwchars.add(chr);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int charsSize = chrTotal;
|
||||
int unk = charsSize + (3 - charsSize % 3); //rowSize?
|
||||
c.announce(MaplePacketCreator.showAllCharacter(charsSize, unk));
|
||||
|
||||
for (Pair<Integer, List<MapleCharacter>> wchars : worldChars) {
|
||||
c.announce(MaplePacketCreator.showAllCharacterInfo(wchars.getLeft(), wchars.getRight(), ServerConstants.ENABLE_PIC));
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -10,7 +10,7 @@ import tools.MaplePacketCreator;
|
||||
import tools.Randomizer;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class ViewAllPicRegisterHandler extends AbstractMaplePacketHandler { //Gey class name lol
|
||||
public final class ViewAllCharRegisterPicHandler extends AbstractMaplePacketHandler { //Gey class name lol
|
||||
|
||||
|
||||
@Override
|
||||
@@ -20,7 +20,7 @@ public final class ViewAllPicRegisterHandler extends AbstractMaplePacketHandler
|
||||
slea.readInt(); // please don't let the client choose which world they should login
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterid(c.getAccID(), charId)) {
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
return;
|
||||
}
|
||||
@@ -31,7 +31,7 @@ public final class ViewAllPicRegisterHandler extends AbstractMaplePacketHandler
|
||||
return;
|
||||
}
|
||||
|
||||
int channel = Randomizer.rand(0, server.getWorld(c.getWorld()).getChannels().size());
|
||||
int channel = Randomizer.rand(1, server.getWorld(c.getWorld()).getChannels().size());
|
||||
c.setChannel(channel);
|
||||
|
||||
String mac = slea.readMapleAsciiString();
|
||||
+4
-3
@@ -31,7 +31,7 @@ import tools.MaplePacketCreator;
|
||||
import tools.Randomizer;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class PickCharHandler extends AbstractMaplePacketHandler {
|
||||
public final class ViewAllCharSelectedHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
@@ -39,7 +39,7 @@ public final class PickCharHandler extends AbstractMaplePacketHandler {
|
||||
slea.readInt(); // please don't let the client choose which world they should login
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterid(c.getAccID(), charId)) {
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
return;
|
||||
}
|
||||
@@ -58,7 +58,8 @@ public final class PickCharHandler extends AbstractMaplePacketHandler {
|
||||
}
|
||||
|
||||
try {
|
||||
c.setChannel(Randomizer.nextInt(c.getWorldServer().getChannels().size()));
|
||||
int channel = Randomizer.rand(1, c.getWorldServer().getChannels().size());
|
||||
c.setChannel(channel);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
c.setChannel(1);
|
||||
@@ -21,13 +21,13 @@ public class ViewAllCharSelectedWithPicHandler extends AbstractMaplePacketHandle
|
||||
slea.readInt(); // please don't let the client choose which world they should login
|
||||
|
||||
Server server = Server.getInstance();
|
||||
if(!server.haveCharacterid(c.getAccID(), charId)) {
|
||||
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
|
||||
c.getSession().close(true);
|
||||
return;
|
||||
}
|
||||
|
||||
c.setWorld(server.getCharacterWorld(charId));
|
||||
int channel = Randomizer.rand(0, c.getWorldServer().getChannels().size());
|
||||
int channel = Randomizer.rand(1, c.getWorldServer().getChannels().size());
|
||||
c.setChannel(channel);
|
||||
|
||||
String macs = slea.readMapleAsciiString();
|
||||
|
||||
@@ -1,92 +0,0 @@
|
||||
/*
|
||||
This file is part of the OdinMS Maple Story Server
|
||||
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
|
||||
Matthias Butz <matze@odinms.de>
|
||||
Jan Christian Meyer <vimes@odinms.de>
|
||||
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.handlers.login;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.server.Server;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class ViewCharHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
try {
|
||||
short charsNum;
|
||||
List<Integer> worlds;
|
||||
List<MapleCharacter> chars;
|
||||
|
||||
int wlen = Server.getInstance().getWorlds().size();
|
||||
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT world, id FROM characters WHERE accountid = ?")) {
|
||||
ps.setInt(1, c.getAccID());
|
||||
charsNum = 0;
|
||||
worlds = new ArrayList<>();
|
||||
chars = new ArrayList<>();
|
||||
try (ResultSet rs = ps.executeQuery()) {
|
||||
while (rs.next()) {
|
||||
int cworld = rs.getByte("world");
|
||||
if(cworld >= wlen) continue;
|
||||
|
||||
boolean inside = false;
|
||||
for (int w : worlds) {
|
||||
if (w == cworld) {
|
||||
inside = true;
|
||||
}
|
||||
}
|
||||
if (!inside) {
|
||||
worlds.add(cworld);
|
||||
}
|
||||
MapleCharacter chr = MapleCharacter.loadCharFromDB(rs.getInt("id"), c, false);
|
||||
chars.add(chr);
|
||||
charsNum++;
|
||||
}
|
||||
}
|
||||
}
|
||||
int unk = charsNum + 3 - charsNum % 3;
|
||||
c.announce(MaplePacketCreator.showAllCharacter(charsNum, unk));
|
||||
for (Iterator<Integer> it = worlds.iterator(); it.hasNext();) {
|
||||
int w = it.next();
|
||||
List<MapleCharacter> chrsinworld = new ArrayList<>();
|
||||
for (MapleCharacter chr : chars) {
|
||||
if (chr.getWorld() == w) {
|
||||
chrsinworld.add(chr);
|
||||
}
|
||||
}
|
||||
c.announce(MaplePacketCreator.showAllCharacterInfo(w, chrsinworld));
|
||||
}
|
||||
|
||||
con.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +36,7 @@ public class CharacterAutosaverWorker extends BaseWorker implements Runnable {
|
||||
PlayerStorage ps = wserv.getPlayerStorage();
|
||||
for(MapleCharacter chr: ps.getAllCharacters()) {
|
||||
if(chr != null && chr.isLoggedin()) {
|
||||
chr.saveToDB(false);
|
||||
chr.saveCharToDB(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,9 @@ import net.server.Server;
|
||||
public class CharacterDiseaseWorker implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
Server.getInstance().runAnnouncePlayerDiseasesSchedule();
|
||||
Server serv = Server.getInstance();
|
||||
|
||||
serv.updateCurrentTime();
|
||||
serv.runAnnouncePlayerDiseasesSchedule();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2017 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
|
||||
@@ -35,6 +35,7 @@ import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
@@ -43,6 +44,8 @@ import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import tools.locks.MonitoredReentrantLock;
|
||||
@@ -77,7 +80,7 @@ import tools.locks.MonitoredLockType;
|
||||
/**
|
||||
*
|
||||
* @author kevintjuh93
|
||||
* @author Ronan (thread-oriented world schedules)
|
||||
* @author Ronan (thread-oriented world schedules, guild queue, marriages & party chars)
|
||||
*/
|
||||
public class World {
|
||||
|
||||
@@ -94,10 +97,14 @@ public class World {
|
||||
private Map<Integer, MapleGuildSummary> gsStore = new HashMap<>();
|
||||
private PlayerStorage players = new PlayerStorage();
|
||||
|
||||
private Map<Integer, SortedMap<Integer, MapleCharacter>> accountChars = new HashMap<>();
|
||||
private Lock accountCharsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_CHARS, true);
|
||||
|
||||
private Set<Integer> queuedGuilds = new HashSet<>();
|
||||
private Map<Integer, Pair<Pair<Boolean, Boolean>, Pair<Integer, Integer>>> queuedMarriages = new HashMap<>();
|
||||
private Map<Integer, Set<Integer>> marriageGuests = new HashMap<>();
|
||||
|
||||
private Map<Integer, Integer> partyChars = new HashMap<>();
|
||||
private Map<Integer, MapleParty> parties = new HashMap<>();
|
||||
private AtomicInteger runningPartyId = new AtomicInteger();
|
||||
private Lock partyLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PARTY, true);
|
||||
@@ -242,6 +249,87 @@ public class World {
|
||||
this.questrate = quest;
|
||||
}
|
||||
|
||||
public void loadAccountCharactersView(Integer accountId, List<MapleCharacter> chars) {
|
||||
SortedMap<Integer, MapleCharacter> charsMap = new TreeMap<>();
|
||||
for(MapleCharacter chr : chars) {
|
||||
charsMap.put(chr.getId(), chr);
|
||||
}
|
||||
|
||||
accountCharsLock.lock(); // accountCharsLock should be used after server's lgnWLock for compliance
|
||||
try {
|
||||
accountChars.put(accountId, charsMap);
|
||||
} finally {
|
||||
accountCharsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void registerAccountCharacterView(Integer accountId, MapleCharacter chr) {
|
||||
accountCharsLock.lock();
|
||||
try {
|
||||
accountChars.get(accountId).put(chr.getId(), chr);
|
||||
} finally {
|
||||
accountCharsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public void unregisterAccountCharacterView(Integer accountId, Integer chrId) {
|
||||
accountCharsLock.lock();
|
||||
try {
|
||||
accountChars.get(accountId).remove(chrId);
|
||||
} finally {
|
||||
accountCharsLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Entry<Integer, SortedMap<Integer, MapleCharacter>>> getSortedAccountCharacterView(Map<Integer, SortedMap<Integer, MapleCharacter>> map) {
|
||||
List<Entry<Integer, SortedMap<Integer, MapleCharacter>>> list = new ArrayList<>(map.size());
|
||||
for(Entry<Integer, SortedMap<Integer, MapleCharacter>> e : map.entrySet()) {
|
||||
list.add(e);
|
||||
}
|
||||
|
||||
Collections.sort(list, new Comparator<Entry<Integer, SortedMap<Integer, MapleCharacter>>>() {
|
||||
@Override
|
||||
public int compare(Entry<Integer, SortedMap<Integer, MapleCharacter>> o1, Entry<Integer, SortedMap<Integer, MapleCharacter>> o2) {
|
||||
return o1.getKey() - o2.getKey();
|
||||
}
|
||||
});
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public List<MapleCharacter> getAllCharactersView() { // sorted by accountid, charid
|
||||
List<MapleCharacter> chrList = new LinkedList<>();
|
||||
Map<Integer, SortedMap<Integer, MapleCharacter>> accChars;
|
||||
|
||||
accountCharsLock.lock();
|
||||
try {
|
||||
accChars = new HashMap<>(accountChars);
|
||||
} finally {
|
||||
accountCharsLock.unlock();
|
||||
}
|
||||
|
||||
for (Entry<Integer, SortedMap<Integer, MapleCharacter>> e : getSortedAccountCharacterView(accChars)) {
|
||||
for (MapleCharacter chr : e.getValue().values()) {
|
||||
chrList.add(chr);
|
||||
}
|
||||
}
|
||||
|
||||
return chrList;
|
||||
}
|
||||
|
||||
public List<MapleCharacter> getAccountCharactersView(Integer accountId) {
|
||||
List<MapleCharacter> chrList;
|
||||
|
||||
accountCharsLock.lock();
|
||||
try {
|
||||
chrList = new LinkedList<>(accountChars.get(accountId).values());
|
||||
} finally {
|
||||
accountCharsLock.unlock();
|
||||
}
|
||||
|
||||
return chrList;
|
||||
}
|
||||
|
||||
public PlayerStorage getPlayerStorage() {
|
||||
return players;
|
||||
}
|
||||
@@ -511,6 +599,37 @@ public class World {
|
||||
System.out.println("Guest list: " + marriageGuests);
|
||||
}
|
||||
|
||||
private void registerCharacterParty(Integer chrid, Integer partyid) {
|
||||
partyLock.lock();
|
||||
try {
|
||||
partyChars.put(chrid, partyid);
|
||||
} finally {
|
||||
partyLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
private void unregisterCharacterPartyInternal(Integer chrid) {
|
||||
partyChars.remove(chrid);
|
||||
}
|
||||
|
||||
private void unregisterCharacterParty(Integer chrid) {
|
||||
partyLock.lock();
|
||||
try {
|
||||
unregisterCharacterPartyInternal(chrid);
|
||||
} finally {
|
||||
partyLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public Integer getCharacterPartyid(Integer chrid) {
|
||||
partyLock.lock();
|
||||
try {
|
||||
return partyChars.get(chrid);
|
||||
} finally {
|
||||
partyLock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
public MapleParty createParty(MaplePartyCharacter chrfor) {
|
||||
int partyid = runningPartyId.getAndIncrement();
|
||||
MapleParty party = new MapleParty(partyid, chrfor);
|
||||
@@ -518,6 +637,7 @@ public class World {
|
||||
partyLock.lock();
|
||||
try {
|
||||
parties.put(party.getId(), party);
|
||||
registerCharacterParty(chrfor.getId(), partyid);
|
||||
} finally {
|
||||
partyLock.unlock();
|
||||
}
|
||||
@@ -534,7 +654,7 @@ public class World {
|
||||
}
|
||||
}
|
||||
|
||||
public MapleParty disbandParty(int partyid) {
|
||||
private MapleParty disbandParty(int partyid) {
|
||||
partyLock.lock();
|
||||
try {
|
||||
return parties.remove(partyid);
|
||||
@@ -543,8 +663,38 @@ public class World {
|
||||
}
|
||||
}
|
||||
|
||||
public void updateParty(MapleParty party, PartyOperation operation, MaplePartyCharacter target) {
|
||||
for (MaplePartyCharacter partychar : party.getMembers()) {
|
||||
private void updateCharacterParty(MapleParty party, PartyOperation operation, MaplePartyCharacter target, Collection<MaplePartyCharacter> partyMembers) {
|
||||
switch (operation) {
|
||||
case JOIN:
|
||||
registerCharacterParty(target.getId(), party.getId());
|
||||
break;
|
||||
|
||||
case LEAVE:
|
||||
case EXPEL:
|
||||
unregisterCharacterParty(target.getId());
|
||||
break;
|
||||
|
||||
case DISBAND:
|
||||
partyLock.lock();
|
||||
try {
|
||||
for (MaplePartyCharacter partychar : partyMembers) {
|
||||
unregisterCharacterPartyInternal(partychar.getId());
|
||||
}
|
||||
} finally {
|
||||
partyLock.unlock();
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateParty(MapleParty party, PartyOperation operation, MaplePartyCharacter target) {
|
||||
Collection<MaplePartyCharacter> partyMembers = party.getMembers();
|
||||
updateCharacterParty(party, operation, target, partyMembers);
|
||||
|
||||
for (MaplePartyCharacter partychar : partyMembers) {
|
||||
MapleCharacter chr = getPlayerStorage().getCharacterByName(partychar.getName());
|
||||
if (chr != null) {
|
||||
if (operation == PartyOperation.DISBAND) {
|
||||
|
||||
@@ -495,7 +495,6 @@ public class AbstractPlayerInteraction {
|
||||
}
|
||||
if (ItemConstants.getInventoryType(id) == MapleInventoryType.EQUIP) {
|
||||
if (randomStats) {
|
||||
item = ii.randomizeStats((Equip) item);
|
||||
MapleInventoryManipulator.addFromDrop(c, ii.randomizeStats((Equip) item), false, petId);
|
||||
} else {
|
||||
MapleInventoryManipulator.addFromDrop(c, (Equip) item, false, petId);
|
||||
|
||||
@@ -558,6 +558,19 @@ public class EventInstanceManager {
|
||||
}
|
||||
}
|
||||
|
||||
public void reviveMonster(MapleMonster mob) {
|
||||
try {
|
||||
sL.lock();
|
||||
try {
|
||||
em.getIv().invokeFunction("monsterRevive", this, mob);
|
||||
} finally {
|
||||
sL.unlock();
|
||||
}
|
||||
} catch (ScriptException | NoSuchMethodException ex) {
|
||||
ex.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean revivePlayer(MapleCharacter chr) {
|
||||
try {
|
||||
Object b;
|
||||
|
||||
@@ -317,7 +317,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
|
||||
|
||||
public boolean canSpawnPlayerNpc(int mapid) {
|
||||
MapleCharacter chr = getPlayer();
|
||||
return !ServerConstants.PLAYERNPC_AUTODEPLOY && chr.getLevel() >= chr.getMaxClassLevel() && MaplePlayerNPC.canSpawnPlayerNpc(chr.getName(), mapid);
|
||||
return !ServerConstants.PLAYERNPC_AUTODEPLOY && chr.getLevel() >= chr.getMaxClassLevel() && !chr.isGM() && MaplePlayerNPC.canSpawnPlayerNpc(chr.getName(), mapid);
|
||||
}
|
||||
|
||||
public MaplePlayerNPC getPlayerNPCByScriptid(int scriptId) {
|
||||
|
||||
@@ -27,6 +27,7 @@ import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import net.server.Server;
|
||||
import javax.management.MBeanServer;
|
||||
import javax.management.ObjectName;
|
||||
import tools.FilePrinter;
|
||||
@@ -78,7 +79,9 @@ public class TimerManager implements TimerManagerMBean {
|
||||
|
||||
public Runnable purge() {//Yay?
|
||||
return new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Server.getInstance().forceUpdateCurrentTime();
|
||||
ses.purge();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -56,9 +56,12 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import tools.locks.MonitoredReentrantLock;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.world.World;
|
||||
import net.server.world.MapleParty;
|
||||
import net.server.world.MaplePartyCharacter;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import server.TimerManager;
|
||||
import server.life.MapleLifeFactory.BanishInfo;
|
||||
import server.maps.MapleMap;
|
||||
@@ -128,6 +131,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
this.dropsDisabled = true;
|
||||
}
|
||||
|
||||
public void enableDrops() {
|
||||
this.dropsDisabled = false;
|
||||
}
|
||||
|
||||
public boolean dropsDisabled() {
|
||||
return dropsDisabled;
|
||||
}
|
||||
@@ -394,12 +401,13 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
public void distributeExperience(int killerId) {
|
||||
private void distributeExperience(int killerId) {
|
||||
if (isAlive()) {
|
||||
return;
|
||||
}
|
||||
|
||||
int minThresholdLevel = calcThresholdLevel(this.getMap().getEventInstance() != null);
|
||||
EventInstanceManager eim = getMap().getEventInstance();
|
||||
int minThresholdLevel = calcThresholdLevel(eim != null);
|
||||
int exp = getExp();
|
||||
long totalHealth = maxHpPlusHeal.get();
|
||||
Map<Integer, Float> expDist = new HashMap<>();
|
||||
@@ -412,12 +420,19 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
expDist.put(damage.getKey(), exp8perHp * damage.getValue().get());
|
||||
}
|
||||
|
||||
Collection<MapleCharacter> chrs = map.getCharacters();
|
||||
Set<MapleCharacter> underleveled = new HashSet<>();
|
||||
for (MapleCharacter mc : chrs) {
|
||||
if (expDist.containsKey(mc.getId())) {
|
||||
Collection<MapleCharacter> mapChrs = map.getCharacters();
|
||||
for (MapleCharacter mc : mapChrs) {
|
||||
Float mcExp = expDist.remove(mc.getId());
|
||||
if (mcExp != null) {
|
||||
boolean isKiller = (mc.getId() == killerId);
|
||||
float xp = expDist.get(mc.getId());
|
||||
if (isKiller) {
|
||||
if (eim != null) {
|
||||
eim.monsterKilled(mc, this);
|
||||
}
|
||||
}
|
||||
|
||||
float xp = mcExp;
|
||||
if (isKiller) {
|
||||
xp += exp2;
|
||||
}
|
||||
@@ -438,6 +453,24 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
if(!expDist.isEmpty()) { // locate on world server the partyid of the missing characters
|
||||
World wserv = Server.getInstance().getWorld(map.getWorld());
|
||||
|
||||
for (Entry<Integer, Float> ed : expDist.entrySet()) {
|
||||
boolean isKiller = (ed.getKey() == killerId);
|
||||
float xp = ed.getValue();
|
||||
if (isKiller) {
|
||||
xp += exp2;
|
||||
}
|
||||
|
||||
Integer pID = wserv.getCharacterPartyid(ed.getKey());
|
||||
if (pID != null) {
|
||||
float pXP = xp + (partyExp.containsKey(pID) ? partyExp.get(pID) : 0);
|
||||
partyExp.put(pID, pXP);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Entry<Integer, Float> party : partyExp.entrySet()) {
|
||||
distributeExperienceToParty(party.getKey(), party.getValue(), killerId, underleveled, minThresholdLevel);
|
||||
}
|
||||
@@ -447,13 +480,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
}
|
||||
|
||||
public void giveExpToCharacter(MapleCharacter attacker, float exp, boolean isKiller, int numExpSharers) {
|
||||
if (isKiller) {
|
||||
if (getMap().getEventInstance() != null) {
|
||||
getMap().getEventInstance().monsterKilled(attacker, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void giveExpToCharacter(MapleCharacter attacker, float exp, boolean isKiller, int numExpSharers) {
|
||||
//PARTY BONUS: 2p -> +2% , 3p -> +4% , 4p -> +6% , 5p -> +8% , 6p -> +10%
|
||||
final float partyModifier = numExpSharers <= 1 ? 0.0f : 0.02f * (numExpSharers - 1);
|
||||
|
||||
@@ -528,6 +555,8 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
}
|
||||
|
||||
if(toSpawn.size() > 0) {
|
||||
final EventInstanceManager eim = this.getMap().getEventInstance();
|
||||
|
||||
TimerManager.getInstance().schedule(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
@@ -564,6 +593,10 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
for(int i = 8810017; i >= 8810010; i--)
|
||||
reviveMap.killMonster(reviveMap.getMonsterById(i), killer, true);
|
||||
}
|
||||
|
||||
if(eim != null) {
|
||||
eim.reviveMonster(mob);
|
||||
}
|
||||
}
|
||||
}
|
||||
}, getAnimationTime("die1"));
|
||||
@@ -599,11 +632,12 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
dispatchUpdateQuestMobCount();
|
||||
}
|
||||
|
||||
if (getMap().getEventInstance() != null) {
|
||||
EventInstanceManager eim = getMap().getEventInstance();
|
||||
if (eim != null) {
|
||||
if (!this.getStats().isFriendly()) {
|
||||
getMap().getEventInstance().monsterKilled(this, hasKiller);
|
||||
eim.monsterKilled(this, hasKiller);
|
||||
} else {
|
||||
getMap().getEventInstance().friendlyKilled(this, hasKiller);
|
||||
eim.friendlyKilled(this, hasKiller);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -403,11 +403,9 @@ public class MaplePlayerNPC extends AbstractMapleMapObject {
|
||||
private static MaplePlayerNPC createPlayerNPCInternal(MapleMap map, Point pos, MapleCharacter chr) {
|
||||
int mapId = map.getId();
|
||||
|
||||
/*
|
||||
if(!canSpawnPlayerNpc(chr.getName(), mapId)) {
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
|
||||
byte branch = GameConstants.getHallOfFameBranch(chr.getJob(), mapId);
|
||||
|
||||
@@ -613,39 +611,10 @@ public class MaplePlayerNPC extends AbstractMapleMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
private static List<Integer> loadCharacteridsFromDB(int world) {
|
||||
List<Integer> list = new LinkedList<>();
|
||||
|
||||
try {
|
||||
Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("SELECT id FROM characters WHERE world = ?");
|
||||
ps.setInt(1, world);
|
||||
|
||||
ResultSet rs = ps.executeQuery();
|
||||
while(rs.next()) {
|
||||
list.add(rs.getInt(1));
|
||||
}
|
||||
|
||||
rs.close();
|
||||
ps.close();
|
||||
con.close();
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
public static void multicastSpawnPlayerNPC(int mapid, int world) {
|
||||
MapleClient mockClient = new MapleClient(null, null, null);
|
||||
|
||||
for(Integer cid : loadCharacteridsFromDB(world)) {
|
||||
try {
|
||||
MapleCharacter mc = MapleCharacter.loadCharFromDB(cid, mockClient, false);
|
||||
World wserv = Server.getInstance().getWorld(world);
|
||||
for(MapleCharacter mc : wserv.getAllCharactersView()) {
|
||||
spawnPlayerNPC(mapid, mc);
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -309,7 +309,7 @@ public class MobSkill {
|
||||
if (lt != null && rb != null && skill) {
|
||||
int i = 0;
|
||||
for (MapleCharacter character : getPlayersInRange(monster, player)) {
|
||||
if (!character.isActiveBuffedValue(2321005)) {
|
||||
if (!character.isActiveBuffedValue(2321005)) { // holy shield
|
||||
if (disease.equals(MapleDisease.SEDUCE)) {
|
||||
if (i < 10) {
|
||||
character.giveDebuff(MapleDisease.SEDUCE, this);
|
||||
@@ -329,9 +329,7 @@ public class MobSkill {
|
||||
}
|
||||
|
||||
private List<MapleCharacter> getPlayersInRange(MapleMonster monster, MapleCharacter player) {
|
||||
List<MapleCharacter> players = new ArrayList<MapleCharacter>();
|
||||
players.add(player);
|
||||
return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), players);
|
||||
return monster.getMap().getPlayersInRange(calculateBoundingBox(monster.getPosition(), monster.isFacingLeft()), Collections.singletonList(player));
|
||||
}
|
||||
|
||||
public int getSkillId() {
|
||||
|
||||
@@ -19,11 +19,10 @@
|
||||
*/
|
||||
package server.maps;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import java.awt.Point;
|
||||
|
||||
import tools.MaplePacketCreator;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import tools.MaplePacketCreator;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -49,10 +48,9 @@ public class MapleDoorObject extends AbstractMapleMapObject {
|
||||
toPos = toPosition;
|
||||
}
|
||||
|
||||
public void warp(final MapleCharacter chr, boolean toTown) {
|
||||
public void warp(final MapleCharacter chr) {
|
||||
if (chr.getId() == ownerId || (chr.getParty() != null && chr.getParty().getMemberById(ownerId) != null)) {
|
||||
if(chr.getParty() == null && (to.isLastDoorOwner(chr.getId()) || toTown)) chr.changeMap(to, toPos);
|
||||
else chr.changeMap(to, to.findClosestPlayerSpawnpoint(toPos)); // weird issues happens with party, relocating players elsewhere....
|
||||
chr.changeMap(to, toPos);
|
||||
} else {
|
||||
chr.getClient().announce(MaplePacketCreator.blockedMessage(6));
|
||||
chr.getClient().announce(MaplePacketCreator.enableActions());
|
||||
|
||||
@@ -38,7 +38,7 @@ import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import tools.locks.MonitoredReentrantLock;
|
||||
import net.SendOpcode;
|
||||
import net.opcodes.SendOpcode;
|
||||
import server.MapleItemInformationProvider;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
@@ -22,6 +22,7 @@ public class FilePrinter {
|
||||
SQL_EXCEPTION = "sqlexceptions.txt",
|
||||
PACKET_HANDLER = "PacketHandler/",
|
||||
PORTAL = "portals/",
|
||||
PORTAL_STUCK = "portalblocks/",
|
||||
NPC = "npcs/",
|
||||
INVOCABLE = "invocable/",
|
||||
REACTOR = "reactors/",
|
||||
|
||||
@@ -23,7 +23,7 @@ package tools;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import net.RecvOpcode;
|
||||
import net.opcodes.RecvOpcode;
|
||||
import client.MapleClient;
|
||||
|
||||
/**
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import net.SendOpcode;
|
||||
import net.opcodes.SendOpcode;
|
||||
import net.server.PlayerCoolDownValueHolder;
|
||||
import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
@@ -152,9 +152,10 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(chr.getHair()); // hair
|
||||
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (chr.getPet(i) != null) //Checked GMS.. and your pets stay when going into the cash shop.
|
||||
MaplePet pet = chr.getPet(i);
|
||||
if (pet != null) //Checked GMS.. and your pets stay when going into the cash shop.
|
||||
{
|
||||
mplew.writeLong(chr.getPet(i).getUniqueId());
|
||||
mplew.writeLong(pet.getUniqueId());
|
||||
} else {
|
||||
mplew.writeLong(0);
|
||||
}
|
||||
@@ -1035,8 +1036,26 @@ public class MaplePacketCreator {
|
||||
mplew.writeInt(to.getId());
|
||||
mplew.write(spawnPoint);
|
||||
mplew.writeShort(chr.getHp());
|
||||
mplew.write(0);
|
||||
mplew.writeBool(false);
|
||||
mplew.writeLong(getTime(System.currentTimeMillis()));
|
||||
mplew.writeShort(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] getWarpToMap(MapleMap to, int spawnPoint, Point spawnPosition, MapleCharacter chr) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.SET_FIELD.getValue());
|
||||
mplew.writeInt(chr.getClient().getChannel() - 1);
|
||||
mplew.writeInt(0);//updated
|
||||
mplew.write(0);//updated
|
||||
mplew.writeInt(to.getId());
|
||||
mplew.write(spawnPoint);
|
||||
mplew.writeShort(chr.getHp());
|
||||
mplew.writeBool(true);
|
||||
mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy)
|
||||
mplew.writeInt(spawnPosition.y);
|
||||
mplew.writeLong(getTime(System.currentTimeMillis()));
|
||||
mplew.writeShort(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
@@ -4873,13 +4892,13 @@ public class MaplePacketCreator {
|
||||
public static byte[] showAllCharacter(int chars, int unk) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(11);
|
||||
mplew.writeShort(SendOpcode.VIEW_ALL_CHAR.getValue());
|
||||
mplew.write(1);
|
||||
mplew.write(chars > 0 ? 1 : 5); // 2: already connected to server, 3 : unk error (view-all-characters), 5 : cannot find any
|
||||
mplew.writeInt(chars);
|
||||
mplew.writeInt(unk);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showAllCharacterInfo(int worldid, List<MapleCharacter> chars) {
|
||||
public static byte[] showAllCharacterInfo(int worldid, List<MapleCharacter> chars, boolean usePic) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.VIEW_ALL_CHAR.getValue());
|
||||
mplew.write(0);
|
||||
@@ -4888,6 +4907,7 @@ public class MaplePacketCreator {
|
||||
for (MapleCharacter chr : chars) {
|
||||
addCharEntry(mplew, chr, true);
|
||||
}
|
||||
mplew.write(usePic ? 1 : 2);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ public enum MonitoredLockType {
|
||||
WORLD_PARTY,
|
||||
WORLD_OWL,
|
||||
WORLD_PETS,
|
||||
WORLD_CHARS,
|
||||
WORLD_MOUNTS,
|
||||
WORLD_PSHOPS,
|
||||
WORLD_MERCHS,
|
||||
|
||||
@@ -2489,23 +2489,22 @@
|
||||
<int name="18" value="2000001" />
|
||||
<int name="19" value="2000002" />
|
||||
<int name="20" value="2000003" />
|
||||
<int name="21" value="2060005" />
|
||||
<int name="22" value="2070001" />
|
||||
<int name="23" value="2070003" />
|
||||
<int name="24" value="2382003" />
|
||||
<int name="25" value="4000021" />
|
||||
<int name="26" value="4000024" />
|
||||
<int name="27" value="4001000" />
|
||||
<int name="28" value="4006001" />
|
||||
<int name="29" value="4007001" />
|
||||
<int name="30" value="4007003" />
|
||||
<int name="31" value="4010001" />
|
||||
<int name="32" value="4010002" />
|
||||
<int name="33" value="4020000" />
|
||||
<int name="34" value="4020007" />
|
||||
<int name="35" value="4130011" />
|
||||
<int name="36" value="4130016" />
|
||||
<int name="37" value="4130017" />
|
||||
<int name="21" value="2070001" />
|
||||
<int name="22" value="2070003" />
|
||||
<int name="23" value="2382003" />
|
||||
<int name="24" value="4000021" />
|
||||
<int name="25" value="4000024" />
|
||||
<int name="26" value="4001000" />
|
||||
<int name="27" value="4006001" />
|
||||
<int name="28" value="4007001" />
|
||||
<int name="29" value="4007003" />
|
||||
<int name="30" value="4010001" />
|
||||
<int name="31" value="4010002" />
|
||||
<int name="32" value="4020000" />
|
||||
<int name="33" value="4020007" />
|
||||
<int name="34" value="4130011" />
|
||||
<int name="35" value="4130016" />
|
||||
<int name="36" value="4130017" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 32\nForm : Mammal\nSpecial Trait : -50% Fire Attribute Damage. Vulnerable to Ice Attribute Attacks.\n\nThe Wild Boar was training hard in the mountain plateaus when it was inspired by the heated sunlight which caused a big spark on its back. The Boars' such a fighting spirit has become a great example for all the fighters of Perion. Once in a while, though, the overly zealous Wild Boar would spark too big of a fire on its back and turn itself into a piece of roasted barbecue, offering themselves as great meals for the fighters of Perion. " />
|
||||
</imgdir>
|
||||
@@ -2856,19 +2855,18 @@
|
||||
<int name="18" value="2043214" />
|
||||
<int name="19" value="2049000" />
|
||||
<int name="20" value="2060000" />
|
||||
<int name="21" value="2060005" />
|
||||
<int name="22" value="2061000" />
|
||||
<int name="23" value="2070008" />
|
||||
<int name="24" value="2382017" />
|
||||
<int name="25" value="4000113" />
|
||||
<int name="26" value="4004000" />
|
||||
<int name="27" value="4020000" />
|
||||
<int name="28" value="4020002" />
|
||||
<int name="29" value="4020007" />
|
||||
<int name="30" value="4031992" />
|
||||
<int name="31" value="4130007" />
|
||||
<int name="32" value="4130008" />
|
||||
<int name="33" value="4131011" />
|
||||
<int name="21" value="2061000" />
|
||||
<int name="22" value="2070008" />
|
||||
<int name="23" value="2382017" />
|
||||
<int name="24" value="4000113" />
|
||||
<int name="25" value="4004000" />
|
||||
<int name="26" value="4020000" />
|
||||
<int name="27" value="4020002" />
|
||||
<int name="28" value="4020007" />
|
||||
<int name="29" value="4031992" />
|
||||
<int name="30" value="4130007" />
|
||||
<int name="31" value="4130008" />
|
||||
<int name="32" value="4131011" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 34\nForm : Spirit\n\nThere are particular entities that linger the infinitude of the space-time continuum, where no ordinary creature can reach by ordinary means; Tick is one of them. Capitalizing on the Dimensional Schism, Tick can be found in the deserted Ludibrium Clocktower, feeding on time. " />
|
||||
</imgdir>
|
||||
@@ -2996,21 +2994,20 @@
|
||||
<int name="20" value="2000003" />
|
||||
<int name="21" value="2041010" />
|
||||
<int name="22" value="2044414" />
|
||||
<int name="23" value="2060005" />
|
||||
<int name="24" value="2070003" />
|
||||
<int name="25" value="2070008" />
|
||||
<int name="26" value="2382029" />
|
||||
<int name="27" value="4000021" />
|
||||
<int name="28" value="4000026" />
|
||||
<int name="29" value="4000029" />
|
||||
<int name="30" value="4003004" />
|
||||
<int name="31" value="4010004" />
|
||||
<int name="32" value="4020003" />
|
||||
<int name="33" value="4020004" />
|
||||
<int name="34" value="4030012" />
|
||||
<int name="35" value="4130001" />
|
||||
<int name="36" value="4130008" />
|
||||
<int name="37" value="4130010" />
|
||||
<int name="23" value="2070003" />
|
||||
<int name="24" value="2070008" />
|
||||
<int name="25" value="2382029" />
|
||||
<int name="26" value="4000021" />
|
||||
<int name="27" value="4000026" />
|
||||
<int name="28" value="4000029" />
|
||||
<int name="29" value="4003004" />
|
||||
<int name="30" value="4010004" />
|
||||
<int name="31" value="4020003" />
|
||||
<int name="32" value="4020004" />
|
||||
<int name="33" value="4030012" />
|
||||
<int name="34" value="4130001" />
|
||||
<int name="35" value="4130008" />
|
||||
<int name="36" value="4130010" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 37\nForm : Mammal\n\nA cute monkey monster that loves banana and toys. It apparently was influenced by the magic powers of Ellinia which would cause Lupin to inadvertently cast magic attacks once in a while. It's apparently an extremely stubborn monster that will, once agitated, throw its banana and go after an adventurer to the very end. If you have high magic defenses, then you'll be able to protect yourself from the banana that Lupin may throw at you. " />
|
||||
</imgdir>
|
||||
@@ -11913,14 +11910,13 @@
|
||||
<int name="34" value="2044902" />
|
||||
<int name="35" value="2044904" />
|
||||
<int name="36" value="2070007" />
|
||||
<int name="37" value="2280001" />
|
||||
<int name="38" value="2280013" />
|
||||
<int name="39" value="2290070" />
|
||||
<int name="40" value="2290091" />
|
||||
<int name="41" value="2388017" />
|
||||
<int name="42" value="4001111" />
|
||||
<int name="43" value="4030012" />
|
||||
<int name="44" value="4031906" />
|
||||
<int name="37" value="2280013" />
|
||||
<int name="38" value="2290070" />
|
||||
<int name="39" value="2290091" />
|
||||
<int name="40" value="2388017" />
|
||||
<int name="41" value="4001111" />
|
||||
<int name="42" value="4030012" />
|
||||
<int name="43" value="4031906" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 100\nForm : Devil\nSpecial Trait : Resistant to Poison Attribute Attacks. Vulnerable to Sanctity Attribute Attacks.\n\nA monster that attacks the ship of adventurers near the continent of Ossyria. It looks similar to the Jr. Balrogs that are found near Victoria Island, but the Crimson Balrogs fly using their huge wings. Some say that the habitat of Crimson Balrogs is an island like Orbis, but such claim has not been confirmed. Due to its force of attack and magic skills that will surpass your expectation, it is suggested that you evade to a safe area rather than hastily attack back should you find yourself in the midst of their attack. They are vulnerable to sanctity attribute." />
|
||||
</imgdir>
|
||||
@@ -11953,26 +11949,25 @@
|
||||
<int name="21" value="2041043" />
|
||||
<int name="22" value="2044902" />
|
||||
<int name="23" value="2044904" />
|
||||
<int name="24" value="2280011" />
|
||||
<int name="25" value="2290042" />
|
||||
<int name="26" value="2290053" />
|
||||
<int name="27" value="2290073" />
|
||||
<int name="28" value="2290102" />
|
||||
<int name="29" value="2290118" />
|
||||
<int name="30" value="2386012" />
|
||||
<int name="31" value="4000180" />
|
||||
<int name="32" value="4004002" />
|
||||
<int name="33" value="4006000" />
|
||||
<int name="34" value="4006001" />
|
||||
<int name="35" value="4010005" />
|
||||
<int name="36" value="4030012" />
|
||||
<int name="37" value="4031252" />
|
||||
<int name="38" value="4031254" />
|
||||
<int name="39" value="4031255" />
|
||||
<int name="40" value="4130004" />
|
||||
<int name="41" value="4130006" />
|
||||
<int name="42" value="4130007" />
|
||||
<int name="43" value="4161018" />
|
||||
<int name="24" value="2290042" />
|
||||
<int name="25" value="2290053" />
|
||||
<int name="26" value="2290073" />
|
||||
<int name="27" value="2290102" />
|
||||
<int name="28" value="2290118" />
|
||||
<int name="29" value="2386012" />
|
||||
<int name="30" value="4000180" />
|
||||
<int name="31" value="4004002" />
|
||||
<int name="32" value="4006000" />
|
||||
<int name="33" value="4006001" />
|
||||
<int name="34" value="4010005" />
|
||||
<int name="35" value="4030012" />
|
||||
<int name="36" value="4031252" />
|
||||
<int name="37" value="4031254" />
|
||||
<int name="38" value="4031255" />
|
||||
<int name="39" value="4130004" />
|
||||
<int name="40" value="4130006" />
|
||||
<int name="41" value="4130007" />
|
||||
<int name="42" value="4161018" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 100\nForm : Fish\nSpecial Trait : -50% Fire Attribute Damage.\n\nA shark found in the deep waters of Aqua Road. The Shark turned even more ferocious as the purifying force of Aqua Road weakened. It attacks with its sharp teeth that can inflict deadly injuries, and some daring adventurers hunt these Sharks to collect their teeth as a trophy." />
|
||||
</imgdir>
|
||||
@@ -12743,9 +12738,8 @@
|
||||
<int name="48" value="4007007" />
|
||||
<int name="49" value="4010001" />
|
||||
<int name="50" value="4020001" />
|
||||
<int name="51" value="4031461" />
|
||||
<int name="52" value="4130001" />
|
||||
<int name="53" value="4130014" />
|
||||
<int name="51" value="4130001" />
|
||||
<int name="52" value="4130014" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 110\nForm : Immortal\nSpecial Trait : Vulnerable to Sanctity Attribute Attacks.\n\nA group of skeletons that belong to the dragon family. The Skelegons can be found roaming around the dragon nests, unaware that they have died and have become Undeads. They are the reincarnation of dead dragons who were killed during the Dragon War, and will attack anything that comes near regardless of whether the approaching enemy is a human, a dragon, or any other forms of life. " />
|
||||
</imgdir>
|
||||
@@ -12782,33 +12776,32 @@
|
||||
<int name="22" value="2044201" />
|
||||
<int name="23" value="2044214" />
|
||||
<int name="24" value="2044801" />
|
||||
<int name="25" value="2280000" />
|
||||
<int name="26" value="2290002" />
|
||||
<int name="27" value="2290009" />
|
||||
<int name="28" value="2290021" />
|
||||
<int name="29" value="2290034" />
|
||||
<int name="30" value="2290041" />
|
||||
<int name="31" value="2290052" />
|
||||
<int name="32" value="2290053" />
|
||||
<int name="33" value="2290058" />
|
||||
<int name="34" value="2290068" />
|
||||
<int name="35" value="2290071" />
|
||||
<int name="36" value="2290073" />
|
||||
<int name="37" value="2290090" />
|
||||
<int name="38" value="2290112" />
|
||||
<int name="39" value="2290121" />
|
||||
<int name="40" value="2290130" />
|
||||
<int name="41" value="2387003" />
|
||||
<int name="42" value="4000244" />
|
||||
<int name="43" value="4000245" />
|
||||
<int name="44" value="4000273" />
|
||||
<int name="45" value="4004004" />
|
||||
<int name="46" value="4006000" />
|
||||
<int name="47" value="4006001" />
|
||||
<int name="48" value="4020002" />
|
||||
<int name="49" value="4020006" />
|
||||
<int name="50" value="4130010" />
|
||||
<int name="51" value="4130022" />
|
||||
<int name="25" value="2290002" />
|
||||
<int name="26" value="2290009" />
|
||||
<int name="27" value="2290021" />
|
||||
<int name="28" value="2290034" />
|
||||
<int name="29" value="2290041" />
|
||||
<int name="30" value="2290052" />
|
||||
<int name="31" value="2290053" />
|
||||
<int name="32" value="2290058" />
|
||||
<int name="33" value="2290068" />
|
||||
<int name="34" value="2290071" />
|
||||
<int name="35" value="2290073" />
|
||||
<int name="36" value="2290090" />
|
||||
<int name="37" value="2290112" />
|
||||
<int name="38" value="2290121" />
|
||||
<int name="39" value="2290130" />
|
||||
<int name="40" value="2387003" />
|
||||
<int name="41" value="4000244" />
|
||||
<int name="42" value="4000245" />
|
||||
<int name="43" value="4000273" />
|
||||
<int name="44" value="4004004" />
|
||||
<int name="45" value="4006000" />
|
||||
<int name="46" value="4006001" />
|
||||
<int name="47" value="4020002" />
|
||||
<int name="48" value="4020006" />
|
||||
<int name="49" value="4130010" />
|
||||
<int name="50" value="4130022" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 113\nForm : Immortal\nSpecial Trait : Vulnerable to Sanctity Attribute Attacks.\n\nA group of skeletons that belong to the dragon family. The Skelegons can be found roaming around the dragon nests, unaware that they have died and have become Undeads. They are the reincarnation of dead dragons who were killed during the Dragon War, and will attack anything that comes near regardless of whether the approaching enemy is a human, a dragon, or any other forms of life. Looking at the cane that they hold in one hand, they are suspected to have been a group of old veteran dragons." />
|
||||
</imgdir>
|
||||
@@ -13044,19 +13037,18 @@
|
||||
<int name="12" value="2040932" />
|
||||
<int name="13" value="2044012" />
|
||||
<int name="14" value="2044013" />
|
||||
<int name="15" value="2280011" />
|
||||
<int name="16" value="2280014" />
|
||||
<int name="17" value="2290078" />
|
||||
<int name="18" value="2290079" />
|
||||
<int name="19" value="2290095" />
|
||||
<int name="20" value="2387006" />
|
||||
<int name="21" value="4000449" />
|
||||
<int name="22" value="4000451" />
|
||||
<int name="23" value="4004000" />
|
||||
<int name="24" value="4010004" />
|
||||
<int name="25" value="4020001" />
|
||||
<int name="26" value="4020009" />
|
||||
<int name="27" value="4130005" />
|
||||
<int name="15" value="2280014" />
|
||||
<int name="16" value="2290078" />
|
||||
<int name="17" value="2290079" />
|
||||
<int name="18" value="2290095" />
|
||||
<int name="19" value="2387006" />
|
||||
<int name="20" value="4000449" />
|
||||
<int name="21" value="4000451" />
|
||||
<int name="22" value="4004000" />
|
||||
<int name="23" value="4010004" />
|
||||
<int name="24" value="4020001" />
|
||||
<int name="25" value="4020009" />
|
||||
<int name="26" value="4130005" />
|
||||
</imgdir>
|
||||
<string name="episode" value="Level : Lv.106\nForm : Spirit\n\nThe Qualm Monks stand at the Road of Regrets, beyond the Gate of the Past, inside the Temple of Time. They serve Rhinne, the Goddess of Time. In order to never have any regrets, they wear a neutral mask to restrain all emotions. However, no one knows what they are really feeling beneath those masks." />
|
||||
</imgdir>
|
||||
@@ -13297,24 +13289,23 @@
|
||||
<int name="9" value="2022003" />
|
||||
<int name="10" value="2040501" />
|
||||
<int name="11" value="2043701" />
|
||||
<int name="12" value="2280002" />
|
||||
<int name="13" value="2290041" />
|
||||
<int name="14" value="2290092" />
|
||||
<int name="15" value="2290093" />
|
||||
<int name="16" value="2290115" />
|
||||
<int name="17" value="2290137" />
|
||||
<int name="18" value="2290139" />
|
||||
<int name="19" value="2387013" />
|
||||
<int name="20" value="4000458" />
|
||||
<int name="21" value="4000459" />
|
||||
<int name="22" value="4004002" />
|
||||
<int name="23" value="4007001" />
|
||||
<int name="24" value="4007007" />
|
||||
<int name="25" value="4010000" />
|
||||
<int name="26" value="4020000" />
|
||||
<int name="27" value="4020009" />
|
||||
<int name="28" value="4130003" />
|
||||
<int name="29" value="4130012" />
|
||||
<int name="12" value="2290041" />
|
||||
<int name="13" value="2290092" />
|
||||
<int name="14" value="2290093" />
|
||||
<int name="15" value="2290115" />
|
||||
<int name="16" value="2290137" />
|
||||
<int name="17" value="2290139" />
|
||||
<int name="18" value="2387013" />
|
||||
<int name="19" value="4000458" />
|
||||
<int name="20" value="4000459" />
|
||||
<int name="21" value="4004002" />
|
||||
<int name="22" value="4007001" />
|
||||
<int name="23" value="4007007" />
|
||||
<int name="24" value="4010000" />
|
||||
<int name="25" value="4020000" />
|
||||
<int name="26" value="4020009" />
|
||||
<int name="27" value="4130003" />
|
||||
<int name="28" value="4130012" />
|
||||
</imgdir>
|
||||
<string name="episode" value="Level : Lv.131\nForm : Spirit\n\nThe chief of the Oblivion Guardians stands at the Road to Oblivion, beyond the Gate of the Past, inside the Temple of Time. Its sole purpose is to protect Rhinne, the Goddess of Time. Compared to other guardians, its armor seems more sturdy and brilliantly decorated. It once was a human knight, but throughout the many years of protecting the goddess inside the temple, it has cast off its humanness. Now it only exists to serve the goddess. The only evidence left of its lost humanity is its shriveled heart that remains as an empty shell of lost memories." />
|
||||
</imgdir>
|
||||
@@ -13752,10 +13743,9 @@
|
||||
<int name="28" value="2044501" />
|
||||
<int name="29" value="2044601" />
|
||||
<int name="30" value="2044701" />
|
||||
<int name="31" value="2280012" />
|
||||
<int name="32" value="2290031" />
|
||||
<int name="33" value="2290129" />
|
||||
<int name="34" value="2388054" />
|
||||
<int name="31" value="2290031" />
|
||||
<int name="32" value="2290129" />
|
||||
<int name="33" value="2388054" />
|
||||
</imgdir>
|
||||
<string name="episode" value="레벨 : Lv.85\n형태 : 무기물형\n\n빅토리아중앙던전의 개미굴광장에는 유명한 노점상인이 있다. 하지만 그 보다 더 깊은 던전에 가끔씩 정체불명의 포장마차가 문을 연다는데... 더 특이한 점은 이 포장마차에서 물건을 사 본 사람은 아직 한 명도 없다는 것이다. 그 이유는 가까이 다가가기만 하면 갑자기 태도가 180도 달라지기 때문이라는데... 개미굴 광장의 노점상인은 경쟁자의 출현에 심기가 불편한것 같다." />
|
||||
</imgdir>
|
||||
@@ -14234,15 +14224,14 @@
|
||||
<int name="0" value="990000900" />
|
||||
</imgdir>
|
||||
<imgdir name="reward">
|
||||
<int name="0" value="2280003" />
|
||||
<int name="1" value="2280015" />
|
||||
<int name="2" value="2290026" />
|
||||
<int name="3" value="2290064" />
|
||||
<int name="4" value="2290075" />
|
||||
<int name="5" value="2290093" />
|
||||
<int name="6" value="2290096" />
|
||||
<int name="7" value="2388021" />
|
||||
<int name="8" value="4001024" />
|
||||
<int name="0" value="2280015" />
|
||||
<int name="1" value="2290026" />
|
||||
<int name="2" value="2290064" />
|
||||
<int name="3" value="2290075" />
|
||||
<int name="4" value="2290093" />
|
||||
<int name="5" value="2290096" />
|
||||
<int name="6" value="2388021" />
|
||||
<int name="7" value="4001024" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : Unknown\nForm : Immortal\nSpecial Trait : -50% Damage of All Attributes.\n\nSharen III, the ruler of the great empire that once existed in the highlands known as Sharenian, called out this evil spirit to guard the legendary jewel, Rubian. Ergoth, whom Sharen III revived, betrayed Sharen III by using the power of Rubian to destroy Sharenian and building his own empire on top of the remains. After a long time passed, the ruins of Sharenian was discovered, and the people who went inside to explore laid their hands on Rubian; this awakened Ergoth, who had been asleep for a long time." />
|
||||
</imgdir>
|
||||
|
||||
@@ -429,7 +429,7 @@ public class MapleQuestItemFetcher {
|
||||
printWriter.println();
|
||||
}
|
||||
|
||||
static private List<Entry<Integer, Integer>> getSortedMapEntries0(Map<Integer, Integer> map) {
|
||||
private static List<Entry<Integer, Integer>> getSortedMapEntries0(Map<Integer, Integer> map) {
|
||||
List<Entry<Integer, Integer>> list = new ArrayList<>(map.size());
|
||||
for(Entry<Integer, Integer> e : map.entrySet()) {
|
||||
list.add(e);
|
||||
@@ -445,7 +445,7 @@ public class MapleQuestItemFetcher {
|
||||
return list;
|
||||
}
|
||||
|
||||
static private List<Entry<Integer, int[]>> getSortedMapEntries1(Map<Integer, int[]> map) {
|
||||
private static List<Entry<Integer, int[]>> getSortedMapEntries1(Map<Integer, int[]> map) {
|
||||
List<Entry<Integer, int[]>> list = new ArrayList<>(map.size());
|
||||
for(Entry<Integer, int[]> e : map.entrySet()) {
|
||||
list.add(e);
|
||||
@@ -461,7 +461,7 @@ public class MapleQuestItemFetcher {
|
||||
return list;
|
||||
}
|
||||
|
||||
static private List<Pair<Integer, List<Integer>>> getSortedMapEntries2(Map<Integer, Set<Integer>> map) {
|
||||
private static List<Pair<Integer, List<Integer>>> getSortedMapEntries2(Map<Integer, Set<Integer>> map) {
|
||||
List<Pair<Integer, List<Integer>>> list = new ArrayList<>(map.size());
|
||||
for(Entry<Integer, Set<Integer>> e : map.entrySet()) {
|
||||
List<Integer> il = new ArrayList<>(2);
|
||||
|
||||
@@ -380,7 +380,7 @@ public class MapleQuestlineFetcher {
|
||||
}
|
||||
|
||||
/*
|
||||
static private List<Pair<Integer, List<Integer>>> getSortedMapEntries(Map<Integer, List<Integer>> map) {
|
||||
private static List<Pair<Integer, List<Integer>>> getSortedMapEntries(Map<Integer, List<Integer>> map) {
|
||||
List<Pair<Integer, List<Integer>>> list = new ArrayList<>(map.size());
|
||||
for(Map.Entry<Integer, List<Integer>> e : map.entrySet()) {
|
||||
List<Integer> il = new ArrayList<>(2);
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+1
-56026
File diff suppressed because one or more lines are too long
+461
-23304
File diff suppressed because one or more lines are too long
+136
-147
@@ -2489,23 +2489,22 @@
|
||||
<int name="18" value="2000001" />
|
||||
<int name="19" value="2000002" />
|
||||
<int name="20" value="2000003" />
|
||||
<int name="21" value="2060005" />
|
||||
<int name="22" value="2070001" />
|
||||
<int name="23" value="2070003" />
|
||||
<int name="24" value="2382003" />
|
||||
<int name="25" value="4000021" />
|
||||
<int name="26" value="4000024" />
|
||||
<int name="27" value="4001000" />
|
||||
<int name="28" value="4006001" />
|
||||
<int name="29" value="4007001" />
|
||||
<int name="30" value="4007003" />
|
||||
<int name="31" value="4010001" />
|
||||
<int name="32" value="4010002" />
|
||||
<int name="33" value="4020000" />
|
||||
<int name="34" value="4020007" />
|
||||
<int name="35" value="4130011" />
|
||||
<int name="36" value="4130016" />
|
||||
<int name="37" value="4130017" />
|
||||
<int name="21" value="2070001" />
|
||||
<int name="22" value="2070003" />
|
||||
<int name="23" value="2382003" />
|
||||
<int name="24" value="4000021" />
|
||||
<int name="25" value="4000024" />
|
||||
<int name="26" value="4001000" />
|
||||
<int name="27" value="4006001" />
|
||||
<int name="28" value="4007001" />
|
||||
<int name="29" value="4007003" />
|
||||
<int name="30" value="4010001" />
|
||||
<int name="31" value="4010002" />
|
||||
<int name="32" value="4020000" />
|
||||
<int name="33" value="4020007" />
|
||||
<int name="34" value="4130011" />
|
||||
<int name="35" value="4130016" />
|
||||
<int name="36" value="4130017" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 32\nForm : Mammal\nSpecial Trait : -50% Fire Attribute Damage. Vulnerable to Ice Attribute Attacks.\n\nThe Wild Boar was training hard in the mountain plateaus when it was inspired by the heated sunlight which caused a big spark on its back. The Boars' such a fighting spirit has become a great example for all the fighters of Perion. Once in a while, though, the overly zealous Wild Boar would spark too big of a fire on its back and turn itself into a piece of roasted barbecue, offering themselves as great meals for the fighters of Perion. " />
|
||||
</imgdir>
|
||||
@@ -2856,19 +2855,18 @@
|
||||
<int name="18" value="2043214" />
|
||||
<int name="19" value="2049000" />
|
||||
<int name="20" value="2060000" />
|
||||
<int name="21" value="2060005" />
|
||||
<int name="22" value="2061000" />
|
||||
<int name="23" value="2070008" />
|
||||
<int name="24" value="2382017" />
|
||||
<int name="25" value="4000113" />
|
||||
<int name="26" value="4004000" />
|
||||
<int name="27" value="4020000" />
|
||||
<int name="28" value="4020002" />
|
||||
<int name="29" value="4020007" />
|
||||
<int name="30" value="4031992" />
|
||||
<int name="31" value="4130007" />
|
||||
<int name="32" value="4130008" />
|
||||
<int name="33" value="4131011" />
|
||||
<int name="21" value="2061000" />
|
||||
<int name="22" value="2070008" />
|
||||
<int name="23" value="2382017" />
|
||||
<int name="24" value="4000113" />
|
||||
<int name="25" value="4004000" />
|
||||
<int name="26" value="4020000" />
|
||||
<int name="27" value="4020002" />
|
||||
<int name="28" value="4020007" />
|
||||
<int name="29" value="4031992" />
|
||||
<int name="30" value="4130007" />
|
||||
<int name="31" value="4130008" />
|
||||
<int name="32" value="4131011" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 34\nForm : Spirit\n\nThere are particular entities that linger the infinitude of the space-time continuum, where no ordinary creature can reach by ordinary means; Tick is one of them. Capitalizing on the Dimensional Schism, Tick can be found in the deserted Ludibrium Clocktower, feeding on time. " />
|
||||
</imgdir>
|
||||
@@ -2996,21 +2994,20 @@
|
||||
<int name="20" value="2000003" />
|
||||
<int name="21" value="2041010" />
|
||||
<int name="22" value="2044414" />
|
||||
<int name="23" value="2060005" />
|
||||
<int name="24" value="2070003" />
|
||||
<int name="25" value="2070008" />
|
||||
<int name="26" value="2382029" />
|
||||
<int name="27" value="4000021" />
|
||||
<int name="28" value="4000026" />
|
||||
<int name="29" value="4000029" />
|
||||
<int name="30" value="4003004" />
|
||||
<int name="31" value="4010004" />
|
||||
<int name="32" value="4020003" />
|
||||
<int name="33" value="4020004" />
|
||||
<int name="34" value="4030012" />
|
||||
<int name="35" value="4130001" />
|
||||
<int name="36" value="4130008" />
|
||||
<int name="37" value="4130010" />
|
||||
<int name="23" value="2070003" />
|
||||
<int name="24" value="2070008" />
|
||||
<int name="25" value="2382029" />
|
||||
<int name="26" value="4000021" />
|
||||
<int name="27" value="4000026" />
|
||||
<int name="28" value="4000029" />
|
||||
<int name="29" value="4003004" />
|
||||
<int name="30" value="4010004" />
|
||||
<int name="31" value="4020003" />
|
||||
<int name="32" value="4020004" />
|
||||
<int name="33" value="4030012" />
|
||||
<int name="34" value="4130001" />
|
||||
<int name="35" value="4130008" />
|
||||
<int name="36" value="4130010" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 37\nForm : Mammal\n\nA cute monkey monster that loves banana and toys. It apparently was influenced by the magic powers of Ellinia which would cause Lupin to inadvertently cast magic attacks once in a while. It's apparently an extremely stubborn monster that will, once agitated, throw its banana and go after an adventurer to the very end. If you have high magic defenses, then you'll be able to protect yourself from the banana that Lupin may throw at you. " />
|
||||
</imgdir>
|
||||
@@ -11913,14 +11910,13 @@
|
||||
<int name="34" value="2044902" />
|
||||
<int name="35" value="2044904" />
|
||||
<int name="36" value="2070007" />
|
||||
<int name="37" value="2280001" />
|
||||
<int name="38" value="2280013" />
|
||||
<int name="39" value="2290070" />
|
||||
<int name="40" value="2290091" />
|
||||
<int name="41" value="2388017" />
|
||||
<int name="42" value="4001111" />
|
||||
<int name="43" value="4030012" />
|
||||
<int name="44" value="4031906" />
|
||||
<int name="37" value="2280013" />
|
||||
<int name="38" value="2290070" />
|
||||
<int name="39" value="2290091" />
|
||||
<int name="40" value="2388017" />
|
||||
<int name="41" value="4001111" />
|
||||
<int name="42" value="4030012" />
|
||||
<int name="43" value="4031906" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 100\nForm : Devil\nSpecial Trait : Resistant to Poison Attribute Attacks. Vulnerable to Sanctity Attribute Attacks.\n\nA monster that attacks the ship of adventurers near the continent of Ossyria. It looks similar to the Jr. Balrogs that are found near Victoria Island, but the Crimson Balrogs fly using their huge wings. Some say that the habitat of Crimson Balrogs is an island like Orbis, but such claim has not been confirmed. Due to its force of attack and magic skills that will surpass your expectation, it is suggested that you evade to a safe area rather than hastily attack back should you find yourself in the midst of their attack. They are vulnerable to sanctity attribute." />
|
||||
</imgdir>
|
||||
@@ -11953,26 +11949,25 @@
|
||||
<int name="21" value="2041043" />
|
||||
<int name="22" value="2044902" />
|
||||
<int name="23" value="2044904" />
|
||||
<int name="24" value="2280011" />
|
||||
<int name="25" value="2290042" />
|
||||
<int name="26" value="2290053" />
|
||||
<int name="27" value="2290073" />
|
||||
<int name="28" value="2290102" />
|
||||
<int name="29" value="2290118" />
|
||||
<int name="30" value="2386012" />
|
||||
<int name="31" value="4000180" />
|
||||
<int name="32" value="4004002" />
|
||||
<int name="33" value="4006000" />
|
||||
<int name="34" value="4006001" />
|
||||
<int name="35" value="4010005" />
|
||||
<int name="36" value="4030012" />
|
||||
<int name="37" value="4031252" />
|
||||
<int name="38" value="4031254" />
|
||||
<int name="39" value="4031255" />
|
||||
<int name="40" value="4130004" />
|
||||
<int name="41" value="4130006" />
|
||||
<int name="42" value="4130007" />
|
||||
<int name="43" value="4161018" />
|
||||
<int name="24" value="2290042" />
|
||||
<int name="25" value="2290053" />
|
||||
<int name="26" value="2290073" />
|
||||
<int name="27" value="2290102" />
|
||||
<int name="28" value="2290118" />
|
||||
<int name="29" value="2386012" />
|
||||
<int name="30" value="4000180" />
|
||||
<int name="31" value="4004002" />
|
||||
<int name="32" value="4006000" />
|
||||
<int name="33" value="4006001" />
|
||||
<int name="34" value="4010005" />
|
||||
<int name="35" value="4030012" />
|
||||
<int name="36" value="4031252" />
|
||||
<int name="37" value="4031254" />
|
||||
<int name="38" value="4031255" />
|
||||
<int name="39" value="4130004" />
|
||||
<int name="40" value="4130006" />
|
||||
<int name="41" value="4130007" />
|
||||
<int name="42" value="4161018" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 100\nForm : Fish\nSpecial Trait : -50% Fire Attribute Damage.\n\nA shark found in the deep waters of Aqua Road. The Shark turned even more ferocious as the purifying force of Aqua Road weakened. It attacks with its sharp teeth that can inflict deadly injuries, and some daring adventurers hunt these Sharks to collect their teeth as a trophy." />
|
||||
</imgdir>
|
||||
@@ -12743,9 +12738,8 @@
|
||||
<int name="48" value="4007007" />
|
||||
<int name="49" value="4010001" />
|
||||
<int name="50" value="4020001" />
|
||||
<int name="51" value="4031461" />
|
||||
<int name="52" value="4130001" />
|
||||
<int name="53" value="4130014" />
|
||||
<int name="51" value="4130001" />
|
||||
<int name="52" value="4130014" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 110\nForm : Immortal\nSpecial Trait : Vulnerable to Sanctity Attribute Attacks.\n\nA group of skeletons that belong to the dragon family. The Skelegons can be found roaming around the dragon nests, unaware that they have died and have become Undeads. They are the reincarnation of dead dragons who were killed during the Dragon War, and will attack anything that comes near regardless of whether the approaching enemy is a human, a dragon, or any other forms of life. " />
|
||||
</imgdir>
|
||||
@@ -12782,33 +12776,32 @@
|
||||
<int name="22" value="2044201" />
|
||||
<int name="23" value="2044214" />
|
||||
<int name="24" value="2044801" />
|
||||
<int name="25" value="2280000" />
|
||||
<int name="26" value="2290002" />
|
||||
<int name="27" value="2290009" />
|
||||
<int name="28" value="2290021" />
|
||||
<int name="29" value="2290034" />
|
||||
<int name="30" value="2290041" />
|
||||
<int name="31" value="2290052" />
|
||||
<int name="32" value="2290053" />
|
||||
<int name="33" value="2290058" />
|
||||
<int name="34" value="2290068" />
|
||||
<int name="35" value="2290071" />
|
||||
<int name="36" value="2290073" />
|
||||
<int name="37" value="2290090" />
|
||||
<int name="38" value="2290112" />
|
||||
<int name="39" value="2290121" />
|
||||
<int name="40" value="2290130" />
|
||||
<int name="41" value="2387003" />
|
||||
<int name="42" value="4000244" />
|
||||
<int name="43" value="4000245" />
|
||||
<int name="44" value="4000273" />
|
||||
<int name="45" value="4004004" />
|
||||
<int name="46" value="4006000" />
|
||||
<int name="47" value="4006001" />
|
||||
<int name="48" value="4020002" />
|
||||
<int name="49" value="4020006" />
|
||||
<int name="50" value="4130010" />
|
||||
<int name="51" value="4130022" />
|
||||
<int name="25" value="2290002" />
|
||||
<int name="26" value="2290009" />
|
||||
<int name="27" value="2290021" />
|
||||
<int name="28" value="2290034" />
|
||||
<int name="29" value="2290041" />
|
||||
<int name="30" value="2290052" />
|
||||
<int name="31" value="2290053" />
|
||||
<int name="32" value="2290058" />
|
||||
<int name="33" value="2290068" />
|
||||
<int name="34" value="2290071" />
|
||||
<int name="35" value="2290073" />
|
||||
<int name="36" value="2290090" />
|
||||
<int name="37" value="2290112" />
|
||||
<int name="38" value="2290121" />
|
||||
<int name="39" value="2290130" />
|
||||
<int name="40" value="2387003" />
|
||||
<int name="41" value="4000244" />
|
||||
<int name="42" value="4000245" />
|
||||
<int name="43" value="4000273" />
|
||||
<int name="44" value="4004004" />
|
||||
<int name="45" value="4006000" />
|
||||
<int name="46" value="4006001" />
|
||||
<int name="47" value="4020002" />
|
||||
<int name="48" value="4020006" />
|
||||
<int name="49" value="4130010" />
|
||||
<int name="50" value="4130022" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : 113\nForm : Immortal\nSpecial Trait : Vulnerable to Sanctity Attribute Attacks.\n\nA group of skeletons that belong to the dragon family. The Skelegons can be found roaming around the dragon nests, unaware that they have died and have become Undeads. They are the reincarnation of dead dragons who were killed during the Dragon War, and will attack anything that comes near regardless of whether the approaching enemy is a human, a dragon, or any other forms of life. Looking at the cane that they hold in one hand, they are suspected to have been a group of old veteran dragons." />
|
||||
</imgdir>
|
||||
@@ -13044,19 +13037,18 @@
|
||||
<int name="12" value="2040932" />
|
||||
<int name="13" value="2044012" />
|
||||
<int name="14" value="2044013" />
|
||||
<int name="15" value="2280011" />
|
||||
<int name="16" value="2280014" />
|
||||
<int name="17" value="2290078" />
|
||||
<int name="18" value="2290079" />
|
||||
<int name="19" value="2290095" />
|
||||
<int name="20" value="2387006" />
|
||||
<int name="21" value="4000449" />
|
||||
<int name="22" value="4000451" />
|
||||
<int name="23" value="4004000" />
|
||||
<int name="24" value="4010004" />
|
||||
<int name="25" value="4020001" />
|
||||
<int name="26" value="4020009" />
|
||||
<int name="27" value="4130005" />
|
||||
<int name="15" value="2280014" />
|
||||
<int name="16" value="2290078" />
|
||||
<int name="17" value="2290079" />
|
||||
<int name="18" value="2290095" />
|
||||
<int name="19" value="2387006" />
|
||||
<int name="20" value="4000449" />
|
||||
<int name="21" value="4000451" />
|
||||
<int name="22" value="4004000" />
|
||||
<int name="23" value="4010004" />
|
||||
<int name="24" value="4020001" />
|
||||
<int name="25" value="4020009" />
|
||||
<int name="26" value="4130005" />
|
||||
</imgdir>
|
||||
<string name="episode" value="Level : Lv.106\nForm : Spirit\n\nThe Qualm Monks stand at the Road of Regrets, beyond the Gate of the Past, inside the Temple of Time. They serve Rhinne, the Goddess of Time. In order to never have any regrets, they wear a neutral mask to restrain all emotions. However, no one knows what they are really feeling beneath those masks." />
|
||||
</imgdir>
|
||||
@@ -13297,24 +13289,23 @@
|
||||
<int name="9" value="2022003" />
|
||||
<int name="10" value="2040501" />
|
||||
<int name="11" value="2043701" />
|
||||
<int name="12" value="2280002" />
|
||||
<int name="13" value="2290041" />
|
||||
<int name="14" value="2290092" />
|
||||
<int name="15" value="2290093" />
|
||||
<int name="16" value="2290115" />
|
||||
<int name="17" value="2290137" />
|
||||
<int name="18" value="2290139" />
|
||||
<int name="19" value="2387013" />
|
||||
<int name="20" value="4000458" />
|
||||
<int name="21" value="4000459" />
|
||||
<int name="22" value="4004002" />
|
||||
<int name="23" value="4007001" />
|
||||
<int name="24" value="4007007" />
|
||||
<int name="25" value="4010000" />
|
||||
<int name="26" value="4020000" />
|
||||
<int name="27" value="4020009" />
|
||||
<int name="28" value="4130003" />
|
||||
<int name="29" value="4130012" />
|
||||
<int name="12" value="2290041" />
|
||||
<int name="13" value="2290092" />
|
||||
<int name="14" value="2290093" />
|
||||
<int name="15" value="2290115" />
|
||||
<int name="16" value="2290137" />
|
||||
<int name="17" value="2290139" />
|
||||
<int name="18" value="2387013" />
|
||||
<int name="19" value="4000458" />
|
||||
<int name="20" value="4000459" />
|
||||
<int name="21" value="4004002" />
|
||||
<int name="22" value="4007001" />
|
||||
<int name="23" value="4007007" />
|
||||
<int name="24" value="4010000" />
|
||||
<int name="25" value="4020000" />
|
||||
<int name="26" value="4020009" />
|
||||
<int name="27" value="4130003" />
|
||||
<int name="28" value="4130012" />
|
||||
</imgdir>
|
||||
<string name="episode" value="Level : Lv.131\nForm : Spirit\n\nThe chief of the Oblivion Guardians stands at the Road to Oblivion, beyond the Gate of the Past, inside the Temple of Time. Its sole purpose is to protect Rhinne, the Goddess of Time. Compared to other guardians, its armor seems more sturdy and brilliantly decorated. It once was a human knight, but throughout the many years of protecting the goddess inside the temple, it has cast off its humanness. Now it only exists to serve the goddess. The only evidence left of its lost humanity is its shriveled heart that remains as an empty shell of lost memories." />
|
||||
</imgdir>
|
||||
@@ -13752,10 +13743,9 @@
|
||||
<int name="28" value="2044501" />
|
||||
<int name="29" value="2044601" />
|
||||
<int name="30" value="2044701" />
|
||||
<int name="31" value="2280012" />
|
||||
<int name="32" value="2290031" />
|
||||
<int name="33" value="2290129" />
|
||||
<int name="34" value="2388054" />
|
||||
<int name="31" value="2290031" />
|
||||
<int name="32" value="2290129" />
|
||||
<int name="33" value="2388054" />
|
||||
</imgdir>
|
||||
<string name="episode" value="레벨 : Lv.85\n형태 : 무기물형\n\n빅토리아중앙던전의 개미굴광장에는 유명한 노점상인이 있다. 하지만 그 보다 더 깊은 던전에 가끔씩 정체불명의 포장마차가 문을 연다는데... 더 특이한 점은 이 포장마차에서 물건을 사 본 사람은 아직 한 명도 없다는 것이다. 그 이유는 가까이 다가가기만 하면 갑자기 태도가 180도 달라지기 때문이라는데... 개미굴 광장의 노점상인은 경쟁자의 출현에 심기가 불편한것 같다." />
|
||||
</imgdir>
|
||||
@@ -14234,15 +14224,14 @@
|
||||
<int name="0" value="990000900" />
|
||||
</imgdir>
|
||||
<imgdir name="reward">
|
||||
<int name="0" value="2280003" />
|
||||
<int name="1" value="2280015" />
|
||||
<int name="2" value="2290026" />
|
||||
<int name="3" value="2290064" />
|
||||
<int name="4" value="2290075" />
|
||||
<int name="5" value="2290093" />
|
||||
<int name="6" value="2290096" />
|
||||
<int name="7" value="2388021" />
|
||||
<int name="8" value="4001024" />
|
||||
<int name="0" value="2280015" />
|
||||
<int name="1" value="2290026" />
|
||||
<int name="2" value="2290064" />
|
||||
<int name="3" value="2290075" />
|
||||
<int name="4" value="2290093" />
|
||||
<int name="5" value="2290096" />
|
||||
<int name="6" value="2388021" />
|
||||
<int name="7" value="4001024" />
|
||||
</imgdir>
|
||||
<string name="episode" value=" Lv. : Unknown\nForm : Immortal\nSpecial Trait : -50% Damage of All Attributes.\n\nSharen III, the ruler of the great empire that once existed in the highlands known as Sharenian, called out this evil spirit to guard the legendary jewel, Rubian. Ergoth, whom Sharen III revived, betrayed Sharen III by using the power of Rubian to destroy Sharenian and building his own empire on top of the remains. After a long time passed, the ruins of Sharenian was discovered, and the people who went inside to explore laid their hands on Rubian; this awakened Ergoth, who had been asleep for a long time." />
|
||||
</imgdir>
|
||||
|
||||
Reference in New Issue
Block a user