diff --git a/README.md b/README.md index 4c48aa4152..66bc810125 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ ## Head developer: Ronan C. P. Lana -Besides myself for maintaining this repository, credits are to be given to Nexon(Duh!), the original MapleSolaxia staff and other colaborators, as just some changes/patches on the game were applied by myself, in which some of them diverged from the original v83 patch contents. +Besides myself for maintaining this repository, credits are to be given to Nexon(Duh!), the original MapleSolaxia staff and other colaborators, as just some changes/patches on the game were applied by myself, in which some of them diverged from the original v83 patch contents (alright, not just "some patches" by now since a whole lot of major server core changes have been applied on this development). Regarding distributability and usage of the code presented here: like it was before, this MapleStory server is open-source. By that, it is meant that anyone is **free to install, use, modify and redistribute the contents**, as long as there is **no kind of commercial trading involved** and the **credits to the original creators are maintained** within the codes. diff --git a/docs/issues.txt b/docs/issues.txt index 7b1be7e003..f67c136e9d 100644 --- a/docs/issues.txt +++ b/docs/issues.txt @@ -7,7 +7,6 @@ Vcoc - Freelance Developer Known issues: - Everytime two people click on an npc at the same time, one of them dcs and the other needs to @dispose to talk to the npc. - If multiple people hit boxes/reactors at the same time, they both dc with invalid pointer error. -- Passwords on minirooms are not encoded for players entering/logging into the map. - Some criticals (e.g. from Aran skills) will not show up as crit for other players. - Deadlocks may start appearing if the server stays online long enough with many players logged in. - If there are multiple bosses that shows HPBar on the map, if a player hits more than one the HPBar may start flickering on the screen. diff --git a/docs/mychanges_ptbr.txt b/docs/mychanges_ptbr.txt index 0e94fe5bf8..7a96d18335 100644 --- a/docs/mychanges_ptbr.txt +++ b/docs/mychanges_ptbr.txt @@ -1172,4 +1172,26 @@ Refatorado vários métodos de liberamento de recursos do server, tais como em: Implementado código que ofereçe suporte para abrir novos worlds e channels sob demanda. Adicionado scheduler dedicado para ações de event managers. Corrigido potencial de deadlock em alguns pontos do sistema de schedulers de canais. -Refatorado vários temporizadores utilizados pelo EventManager e Channel, como o respawn de mobs e o disposeInstance. \ No newline at end of file +Refatorado vários temporizadores utilizados pelo EventManager e Channel, como o respawn de mobs e o disposeInstance. + +24 Julho 2018, +Implementado código que ofereçe suporte para remover worlds e channels sob demanda. +Corrigido alguns problemas com deadlocks ao tentar remover worlds e channels. +Melhorado envio de packets de objetos de ambiente. Agora é necessário enviar somente um packet, que lista o estado corrente de todos os obstáculos de um mapa (usado bastante na CWKPQ). +Corrigido mobs não sendo devidamente registrados em eventos e não sumindo em certos cenários (e.g. quando chamado por outros mobs/bosses). + +25 - 26 Julho 2018, +Melhorado sistema de respawn de mobs. Agora a quantidade de mobs em campo depende do número de jogadores no mesmo mapa. +Corrigido certos pontos em MapleClient não liberando devidamente o jogador da estrutura que mantem todos os jogadores online num mundo. +Corrigido corda atuando erroneamente em mapas de Malaysia. +Remodelado viagens para Malaysia, agora por dois trajetos: CBD e Boat Quay. Para voltar de Malaysia somente conversando com Audrey (NPC agente de viagens) agora. +Implementado um temporizador para drops de mobs. Aparentemente GMS drops levavam em conta o tempo de despawn do mob antes de lançar itens em campo. +Corrigido dispose de EIM finalizando incorretamente se a função "dispose" estiver sendo disparado via scripts. +Protegido concorrentemente acesso a módulos de distribuição de APs, evitando assim exploits críticos com APs. +Melhorado desempenho do HealOvertimeHandler por fazendo buscar informação de mapas com recovery rate maior do XML ao invés de testar um conjunto limitado de mapas. + +27 Julho 2018, +Corrigido comando "maxhpmp" permitindo efetivar valores negativos ao alvo. +Corrigido Duey não registrando nível e experiência de equipamentos ao enviá-los. +Buff no buyback: por um curto período após respawnar o jogador não morre (mínimo 1 HP), assim permitindo um tempo mínimo de reação. +Revisado task do item monitor em MapleMap mal-protegido contra acessos concorrentes. \ No newline at end of file diff --git a/scripts/event/0_EXAMPLE.js b/scripts/event/0_EXAMPLE.js index 6e6d98ba06..b542d1a655 100644 --- a/scripts/event/0_EXAMPLE.js +++ b/scripts/event/0_EXAMPLE.js @@ -112,6 +112,10 @@ function playerDead(eim, player) { // Happens when player dies } +function monsterRevive(mob, eim) { + // Happens when an opposing mob revives +} + function playerRevive(eim, player) { // Happens when player's revived. // @Param : returns true/false diff --git a/scripts/npc/1061010.js b/scripts/npc/1061010.js index ba0685ada2..dd640d2da1 100644 --- a/scripts/npc/1061010.js +++ b/scripts/npc/1061010.js @@ -28,8 +28,6 @@ function action(mode, type, selection) { else if(mapid == 108010401) cm.getPlayer().changeMap(107000402); else if(mapid == 108010501) cm.getPlayer().changeMap(105070200); - var em = cm.getEventManager("3rdjob"); - em.getInstance(cm.getPlayer().getName()).unregisterPlayer(cm.getPlayer()); cm.dispose(); } } diff --git a/scripts/npc/2042000.js b/scripts/npc/2042000.js index 1d539fa0b3..5387617294 100644 --- a/scripts/npc/2042000.js +++ b/scripts/npc/2042000.js @@ -80,7 +80,7 @@ function action(mode, type, selection) { if(allDone) { cm.sendOk("Done. Thanks for showing up~."); } else { - cm.sendOk("Done. Be aware some of the items could not be synthetized because either you have a lack of space on your ETC inventory or there's not enough mesos to cover the fee."); + cm.sendOk("Done. Be aware some of the items #rcould not be synthetized#k because either you have a lack of space on your ETC inventory or there's not enough mesos to cover the fee."); } cm.dispose(); } diff --git a/scripts/npc/9000020.js b/scripts/npc/9000020.js index d9fda2f36b..1a35a90e59 100644 --- a/scripts/npc/9000020.js +++ b/scripts/npc/9000020.js @@ -21,10 +21,50 @@ */ status = -1; + +var travelFrom = [777777777, 541000000]; +var travelFee = [3000, 10000]; + +var travelMap = [800000000, 550000000]; +var travelPlace = ["Mushroom Shrine of Japan", "Trend Zone of Malaysia"]; +var travelPlaceShort = ["Mushroom Shrine", "Metropolis"]; +var travelPlaceCountry = ["Japan", "Malaysia"]; +var travelAgent = ["I", "#r#p9201135##k"]; + +var travelDescription = ["If you desire to feel the essence of Japan, there's nothing like visiting the Shrine, a Japanese cultural melting pot. Mushroom Shrine is a mythical place that serves the incomparable Mushroom God from ancient times.", + "If you desire to feel the heat of the tropics on an upbeat environment, the residents of Malaysia are eager to welcome you. Also, the metropolis itself is the heart of the local economy, that place is known to always offer something to do or to visit around."]; + +var travelDescription2 = ["Check out the female shaman serving the Mushroom God, and I strongly recommend trying Takoyaki, Yakisoba, and other delocious food sold in the streets of Japan. Now, let's head over to #bMushroom Shrine#k, a mythical place if there ever was one.", + "Once there, I strongly suggest you to schedule a visit to Kampung Village. Why? Surely you've come to know about the fantasy theme park Spooky World? No? It's simply put the greatest theme park around there, it's worth a visit! Now, let's head over to the #bTrend Zone of Malaysia#k."]; + +var travelType; +var travelStatus; + function start() { + travelStatus = getTravelingStatus(cm.getPlayer().getMapId()); action(1,0,0); } +function getTravelingStatus(mapid) { + for(var i = 0; i < travelMap.length; i++) { + if(mapid == travelMap[i]) { + return i; + } + } + + return -1; +} + +function getTravelType(mapid) { + for(var i = 0; i < travelFrom.length; i++) { + if(mapid == travelFrom[i]) { + return i; + } + } + + return 0; +} + function action(mode, type, selection) { status++; if(mode != 1){ @@ -35,7 +75,8 @@ function action(mode, type, selection) { return; } } - if (cm.getPlayer().getMapId() == 800000000) { + + if (travelStatus != -1) { if (status == 0) cm.sendSimple("How's the traveling? Are you enjoying it?#b\r\n#L0#Yes, I'm done with traveling. Can I go back to #m" + cm.getPlayer().peekSavedLocation("WORLDTOUR") + "#?\r\n#L1#No, I'd like to continue exploring this place."); else if (status == 1) { @@ -53,23 +94,24 @@ function action(mode, type, selection) { cm.dispose(); } } else { - if (status == 0) - cm.sendNext("If you're tired of the monotonous daily life, how about getting out for a change? there's nothing quite like soaking up a new culture, learning something new by the minute! It's time for you to get out and travel. We, at the Maple Travel Agency recommend you going on a #bWorld Tour#k! Are you worried about the travel expense? You shouldn't be! We, the #bMaple Travel Agency#k, havecarefully come up with a plan to let you travel for ONLY #b3,000 mesos#k!"); - else if (status == 1) - cm.sendSimple("We currently offer this place for you traveling pleasure: #bMushroom Shrine of Japan#k. I'll be there serving you as the travel guide. Rest assured, the number of destinations will be increase over time. Now, would you like to head over to the Mushroom Shrine?#b\r\n#L0#Yes, take me to Mushroom Shrine (Japan)"); - else if (status == 2) - cm.sendNext("Would you like to travel to #bMushroom Shrine of Japan#k? If you desire to feel the essence of Japan, there's nothing like visiting the Shrine, a Japanese cultural melting pot. Mushroom Shrine is a mythical place that serves the incomparable Mushroom God from ancient times."); - else if (status == 3) { - if(cm.getMeso() < 3000){ + if (status == 0) { + travelType = getTravelType(cm.getPlayer().getMapId()); + cm.sendNext("If you're tired of the monotonous daily life, how about getting out for a change? there's nothing quite like soaking up a new culture, learning something new by the minute! It's time for you to get out and travel. We, at the Maple Travel Agency recommend you going on a #bWorld Tour#k! Are you worried about the travel expense? You shouldn't be! We, the #bMaple Travel Agency#k, have carefully come up with a plan to let you travel for ONLY #b" + cm.numberWithCommas(travelFee[travelType]) + " mesos#k!"); + } else if (status == 1) { + cm.sendSimple("We currently offer this place for you traveling pleasure: #b" + travelPlace[travelType] + "#k. " + travelAgent[travelType] + "'ll be there serving you as the travel guide. Rest assured, the number of destinations will be increase over time. Now, would you like to head over to the " + travelPlaceShort[travelType] + "?#b\r\n#L0#Yes, take me to " + travelPlaceShort[travelType] + " (" + travelPlaceCountry[travelType] + ")"); + } else if (status == 2) { + cm.sendNext("Would you like to travel to #b" + travelPlace[travelType] + "#k? " + travelDescription[travelType]); + } else if (status == 3) { + if(cm.getMeso() < travelFee[travelType]){ cm.sendNext("You don't have enough mesos to take the travel."); cm.dispose(); return; } - cm.sendNextPrev("Check out the female shaman serving the Mushroom God, and I strongly recommend trying Takoyaki, Yakisoba, and other delocious food sold in the streets of Japan. Now, let's head over to #bMushroom Shrine#k, a mythical place if there ever was one."); + cm.sendNextPrev(travelDescription2[travelType]); } else if (status == 4) { - cm.gainMeso(-3000); + cm.gainMeso(-travelFee[travelType]); cm.getPlayer().saveLocation("WORLDTOUR"); - cm.warp(800000000, 0); + cm.warp(travelMap[travelType], 0); cm.dispose(); } } diff --git a/scripts/npc/9201135.js b/scripts/npc/9201135.js index 66f6bb89d2..84a20758ea 100644 --- a/scripts/npc/9201135.js +++ b/scripts/npc/9201135.js @@ -19,26 +19,55 @@ along with this program. If not, see . */ -var toMap = new Array(550000000, 551000000, 540000000,540000000); -var inMap = new Array(540000000, 540000000, 551000000, 550000000); -var cost = new Array(10000, 50000, 50000, 10000); +var inMap = new Array(540000000, 550000000, 551000000); +var toMap = new Array(550000000, new Array(551000000, 541000000), 550000000); +var cost = new Array(42000, new Array(10000, 0), 10000); +var toMapSp = new Array(0, new Array(2, 4), 4); + var location; -var text = "Where would you like to travel?\n\n"; +var text; + +var travelCost; +var travelMap; +var travelSp; + +var startedTravel = false; + var status = 0; function start() { if (cm.getPlayer().getMap().getId() != 540000000) { - for (var i = 0; i < toMap.length; i ++) { - if (inMap[i] == cm.getPlayer().getMap().getId()) { - location = i; - break; - } - } - text +="\t\r\n#b#L0##m" + toMap[location] + "# (" + cost[location] + "mesos)#l#k"; + text = "Hey I'm #p9201135#, your tour guide here in #rMalaysia#k. Where would you like to travel?\n\n"; } else { - text += "\t\r\n#b#L0##m" + toMap[0] + "# (" + cost[0] + "mesos)#l\n\t\r\n#L1##m" + toMap[1] + "# (" + cost[1] + "mesos)#l#k"; + text = "Hey I'm #p9201135#, a tour guide on #rMalaysia#k. Since you're not registered in our special travel package with our partner #bMaple Travel Agency#k, the ride will be significantly more expensive. So, would you like to ride now?\n\n"; + startedTravel = true; } - cm.sendSimple(text); + + for (var i = 0; i < toMap.length; i ++) { + if (inMap[i] == cm.getPlayer().getMap().getId()) { + if(inMap[i] == 550000000) { + toMap[1][1] = cm.getPlayer().peekSavedLocation("WORLDTOUR"); + } + + location = i; + break; + } + } + + if(toMap[location] instanceof Array) { + var maps = toMap[location]; + var costs = cost[location]; + + for(var i = 0; i < maps.length; i++) { + text +="\t\r\n#b#L" + i + "##m" + maps[i] + "# " + (costs[i] > 0 ? "(" + costs[i] + "mesos)" : "") + "#l"; + } + } else { + text +="\t\r\n#b#L0##m" + toMap[location] + "# " + (cost[location] > 0 ? "(" + cost[location] + "mesos)" : "") + "#l"; + } + + text += "#k"; + + cm.sendSimple(text); } function action(mode, type, selection) { @@ -51,22 +80,45 @@ function action(mode, type, selection) { return; } else { status++; - } + } if (status == 1) { - if (cm.getPlayer().getMap().getId() == 540000000) { - location = selection; - } if (toMap[location] == null) { - cm.dipose(); + cm.dispose(); return; } - cm.sendYesNo("Would you like to travel to #b#m"+toMap[location]+"##k? To head over to #b#m"+toMap[location]+"##k, it'll cost you cost you #b" + cost[location] + "#k. Would you like to go right now?"); + + if(toMap[location] instanceof Array) { + var maps = toMap[location]; + var costs = cost[location]; + var sps = toMapSp[location]; + + travelCost = costs[selection]; + travelMap = maps[selection]; + travelSp = sps[selection]; + } else { + travelCost = cost[location]; + travelMap = toMap[location]; + travelSp = toMapSp[location]; + } + + if(travelCost > 0) { + cm.sendYesNo("Would you like to travel to #b#m" + travelMap + "##k? To head over to #b#m" + travelMap + "##k, it'll cost you #r" + cm.numberWithCommas(travelCost) + " mesos#k. Would you like to go right now?"); + } else { + cm.sendNext("Had a great time in #rMalaysia#k? I hope so, have a safe travel back!"); + } } else if (status == 2) { - if (cm.getMeso() < cost[location]) { + if (cm.getMeso() < travelCost) { cm.sendNext("You do not seem to have enough mesos."); } else { - cm.warp(toMap[location]); - cm.gainMeso(-cost[location]); + if(travelCost > 0) { + cm.gainMeso(-travelCost); + if(startedTravel) cm.getPlayer().saveLocation("WORLDTOUR"); + } + else { + travelMap = cm.getPlayer().getSavedLocation("WORLDTOUR"); + } + + cm.warp(travelMap, travelSp); } cm.dispose(); } diff --git a/scripts/npc/9270033.js b/scripts/npc/9270033.js index fa038e915e..b12b211261 100644 --- a/scripts/npc/9270033.js +++ b/scripts/npc/9270033.js @@ -53,7 +53,7 @@ function action(mode, type, selection) { } } else if(status == 1) { if(eim.isEventCleared()) { - if(!eim.giveEventReward(cm.getPlayer)) { + if(!eim.giveEventReward(cm.getPlayer())) { cm.sendOk("Please make a room on your inventory to receive the loot."); cm.dispose(); return; diff --git a/sql/db_database.sql b/sql/db_database.sql index 6439a3e495..fca11f9286 100644 --- a/sql/db_database.sql +++ b/sql/db_database.sql @@ -12804,6 +12804,8 @@ CREATE TABLE IF NOT EXISTS `dueyitems` ( `quantity` int(10) unsigned NOT NULL DEFAULT '0', `upgradeslots` int(11) DEFAULT '0', `level` int(11) DEFAULT '0', + `itemlevel` int(11) DEFAULT '0', + `itemexp` int(11) DEFAULT '0', `str` int(11) DEFAULT '0', `dex` int(11) DEFAULT '0', `int` int(11) DEFAULT '0', diff --git a/src/client/MapleCharacter.java b/src/client/MapleCharacter.java index f87d2d82fb..bbeb373149 100644 --- a/src/client/MapleCharacter.java +++ b/src/client/MapleCharacter.java @@ -157,7 +157,7 @@ import net.server.channel.handlers.PartyOperationHandler; import scripting.item.ItemScriptManager; import server.maps.MapleMapItem; import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; public class MapleCharacter extends AbstractAnimatedMapleMapObject { private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance(); @@ -275,10 +275,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { private ScheduledFuture extraRecoveryTask = null; private ScheduledFuture chairRecoveryTask = null; private ScheduledFuture pendantOfSpirit = null; //1122017 - private Lock chrLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_CHR, true); - private Lock effLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_EFF, true); - private Lock petLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_PET, true); // for quest tasks as well - private Lock prtLock = new MonitoredReentrantLock(MonitoredLockType.CHARACTER_PRT); + private Lock chrLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_CHR, true); + private Lock effLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_EFF, true); + private Lock petLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PET, true); // for quest tasks as well + private Lock prtLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_PRT); private Map> excluded = new LinkedHashMap<>(); private Set excludedItems = new LinkedHashSet<>(); private static String[] ariantroomleader = new String[3]; @@ -445,8 +445,27 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return awayFromWorld.get(); } - public void setAwayFromWorld(boolean away) { - awayFromWorld.set(away); + public void setEnteredChannelWorld() { + awayFromWorld.set(false); + client.getChannelServer().removePlayerAway(id); + } + + public void setAwayFromChannelWorld() { + setAwayFromChannelWorld(false); + } + + public void setDisconnectedFromChannelWorld() { + setAwayFromChannelWorld(true); + } + + private void setAwayFromChannelWorld(boolean disconnect) { + awayFromWorld.set(true); + + if(!disconnect) { + client.getChannelServer().insertPlayerAway(id); + } else { + client.getChannelServer().removePlayerAway(id); + } } public long getPetLootCd() { @@ -1548,9 +1567,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } // if this map has obstacle components moving, make it do so for this client - for(Entry e: map.getEnvironment().entrySet()) { - announce(MaplePacketCreator.environmentMove(e.getKey(), e.getValue())); - } + announce(MaplePacketCreator.environmentMoveList(map.getEnvironment().entrySet())); } } @@ -5273,6 +5290,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { return lastBuyback + ServerConstants.BUYBACK_COOLDOWN_MINUTES * 60 * 1000; } + private boolean isBuybackInvincible() { + return Server.getInstance().getCurrentTime() - lastBuyback < 4200; + } + private int getBuybackFee() { float fee = ServerConstants.BUYBACK_FEE; int grade = Math.min(Math.max(level, 30), 120) - 30; @@ -5282,10 +5303,10 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { } public boolean couldBuyback() { // Ronan's buyback system - long timeNow = System.currentTimeMillis(); + long timeNow = Server.getInstance().getCurrentTime(); if(timeNow - lastDeathtime > ServerConstants.BUYBACK_RETURN_MINUTES * 60 * 1000) { - this.dropMessage(5, "You are unable to buyback now since the time available to decide has expired."); + this.dropMessage(5, "The time available to decide has expired, therefore you are unable to buyback now."); return false; } @@ -7621,7 +7642,11 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject { updatePartyMemberHP(); } if (oldHp > hp && !isAlive()) { - playerDead(); + if(!isBuybackInvincible()) { + playerDead(); + } else { + this.hp = 1; + } } } diff --git a/src/client/MapleClient.java b/src/client/MapleClient.java index 1d82fb62ba..7028231540 100644 --- a/src/client/MapleClient.java +++ b/src/client/MapleClient.java @@ -75,7 +75,7 @@ import server.maps.*; import server.quest.MapleQuest; import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; public class MapleClient { @@ -107,9 +107,9 @@ public class MapleClient { private int picattempt = 0; private byte gender = -1; private boolean disconnecting = false; - private final Lock lock = new MonitoredReentrantLock(MonitoredLockType.CLIENT, true); - private final Lock encoderLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT_ENCODER, true); - private static final Lock loginLock = new MonitoredReentrantLock(MonitoredLockType.CLIENT_LOGIN, true); + private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true); + private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true); + private static final Lock loginLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_LOGIN, true); private int votePoints; private int voteTime = -1; private long lastNpcClick; @@ -792,7 +792,7 @@ public class MapleClient { private void removePlayer() { try { - player.setAwayFromWorld(true); + player.setDisconnectedFromChannelWorld(); player.notifyMapTransferToPartner(-1); player.cancelAllBuffs(true); player.cancelAllDebuffs(); @@ -836,10 +836,13 @@ public class MapleClient { player.cancelMagicDoor(); + final World wserv = getWorldServer(); if (channel == -1 || shutdown) { if(chrg != null) chrg.setCharacter(null); - + + if(wserv != null) wserv.removePlayer(player); removePlayer(); + player.saveCooldowns(); player.saveCharToDB(true); @@ -849,12 +852,11 @@ public class MapleClient { removePlayer(); - final World worlda = getWorldServer(); try { if (!cashshop) { if (!this.serverTransition) { // meaning not changing channels if (messengerid > 0) { - worlda.leaveMessenger(messengerid, chrm); + wserv.leaveMessenger(messengerid, chrm); } /* if (fid > 0) { @@ -877,7 +879,7 @@ public class MapleClient { } if (party != null) { chrp.setOnline(false); - worlda.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp); + wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp); if (map != null && party.getLeader().getId() == idz) { MaplePartyCharacter lchr = null; for (MaplePartyCharacter pchr : party.getMembers()) { @@ -886,19 +888,19 @@ public class MapleClient { } } if (lchr != null) { - worlda.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr); + wserv.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr); } } } if (bl != null) { - worlda.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); + wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); } } } else { if (!this.serverTransition) { // if dc inside of cash shop. if (party != null) { chrp.setOnline(false); - worlda.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp); + wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp); if (map != null && party.getLeader().getId() == idz) { MaplePartyCharacter lchr = null; for (MaplePartyCharacter pchr : party.getMembers()) { @@ -907,12 +909,12 @@ public class MapleClient { } } if (lchr != null) { - worlda.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr); + wserv.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr); } } } if (bl != null) { - worlda.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); + wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds()); } } } @@ -920,7 +922,7 @@ public class MapleClient { FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e); } finally { if (!this.serverTransition) { - worlda.removePlayer(player); + wserv.removePlayer(player); //getChannelServer().removePlayer(player); already being done player.saveCooldowns(); @@ -1309,7 +1311,15 @@ public class MapleClient { return; } - String[] socket = Server.getInstance().getIP(getWorld(), channel).split(":"); + String[] socket; + try { + socket = Server.getInstance().getIP(getWorld(), channel).split(":"); + } catch (Exception e) { + announce(MaplePacketCreator.serverNotice(1, "Channel " + channel + " is currently disabled. Try another channel.")); + announce(MaplePacketCreator.enableActions()); + return; + } + if (player.getTrade() != null) { MapleTrade.cancelTrade(getPlayer()); } @@ -1325,7 +1335,7 @@ public class MapleClient { player.unregisterChairBuff(); server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs()); server.getPlayerBuffStorage().addDiseasesToStorage(player.getId(), player.getAllDiseases()); - player.setAwayFromWorld(true); + player.setDisconnectedFromChannelWorld(); player.notifyMapTransferToPartner(-1); player.cancelAllBuffs(true); player.cancelAllDebuffs(); diff --git a/src/client/MonsterBook.java b/src/client/MonsterBook.java index 0175f3e633..1d2c6e41e3 100644 --- a/src/client/MonsterBook.java +++ b/src/client/MonsterBook.java @@ -32,10 +32,10 @@ import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.locks.Lock; import java.util.concurrent.Semaphore; -import net.server.audit.locks.MonitoredReentrantLock; import tools.DatabaseConnection; import tools.MaplePacketCreator; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; public final class MonsterBook { private static final Semaphore semaphore = new Semaphore(10); @@ -44,7 +44,7 @@ public final class MonsterBook { private int normalCard = 0; private int bookLevel = 1; private Map cards = new LinkedHashMap<>(); - private Lock lock = new MonitoredReentrantLock(MonitoredLockType.BOOK); + private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.BOOK); private Set> getCardSet() { lock.lock(); diff --git a/src/client/command/Commands.java b/src/client/command/Commands.java index ce9e8453b3..1c5805bb2e 100644 --- a/src/client/command/Commands.java +++ b/src/client/command/Commands.java @@ -1804,29 +1804,31 @@ public class Commands { if (sub.length >= 3) { victim = c.getWorldServer().getPlayerStorage().getCharacterByName(sub[1]); - statUpdate = Integer.valueOf(sub[2]); + statUpdate = Math.max(1, Integer.valueOf(sub[2])); } else if(sub.length == 2) { - statUpdate = Integer.valueOf(sub[1]); + statUpdate = Math.max(1, Integer.valueOf(sub[1])); } else { player.yellowMessage("Syntax: !maxhpmp [] "); } if(victim != null) { + List> statup = new ArrayList<>(4); + if(victim.getHp() > statUpdate) { victim.setHp(statUpdate); - victim.updateSingleStat(MapleStat.HP, statUpdate); + statup.add(new Pair<>(MapleStat.HP, statUpdate)); } + statup.add(new Pair<>(MapleStat.MAXHP, statUpdate)); if(victim.getMp() > statUpdate) { victim.setMp(statUpdate); - victim.updateSingleStat(MapleStat.MP, statUpdate); + statup.add(new Pair<>(MapleStat.MP, statUpdate)); } + statup.add(new Pair<>(MapleStat.MAXMP, statUpdate)); + c.announce(MaplePacketCreator.updatePlayerStats(statup, victim)); victim.setMaxHp(statUpdate); victim.setMaxMp(statUpdate); - victim.updateSingleStat(MapleStat.MAXHP, statUpdate); - victim.updateSingleStat(MapleStat.MAXMP, statUpdate); - victim.checkBerserk(victim.isHidden()); } else { player.message("Player '" + sub[1] + "' could not be found on this world."); @@ -2895,7 +2897,6 @@ public class Commands { break; - /* case "removechannel": if (sub.length < 2) { player.dropMessage(5, "Syntax: @removechannel "); @@ -2929,7 +2930,6 @@ public class Commands { } break; - */ case "shutdown": case "shutdownnow": diff --git a/src/client/inventory/ItemFactory.java b/src/client/inventory/ItemFactory.java index 9399d842b0..eb6661835c 100644 --- a/src/client/inventory/ItemFactory.java +++ b/src/client/inventory/ItemFactory.java @@ -28,10 +28,10 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; import tools.DatabaseConnection; import tools.Pair; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -47,7 +47,7 @@ public enum ItemFactory { MERCHANT(6, false); private final int value; private final boolean account; - private static final Lock lock = new MonitoredReentrantLock(MonitoredLockType.ITEM, true); + private static final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.ITEM, true); private ItemFactory(int value, boolean account) { this.value = value; diff --git a/src/client/inventory/MapleInventory.java b/src/client/inventory/MapleInventory.java index 1963dc9f83..d8f20304fc 100644 --- a/src/client/inventory/MapleInventory.java +++ b/src/client/inventory/MapleInventory.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.Map.Entry; import java.util.Map; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import tools.Pair; import client.MapleCharacter; @@ -53,7 +53,7 @@ public class MapleInventory implements Iterable { protected byte slotLimit; protected MapleInventoryType type; protected boolean checked = false; - protected Lock lock = new MonitoredReentrantLock(MonitoredLockType.INVENTORY, true); + protected Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.INVENTORY, true); public MapleInventory(MapleCharacter mc, MapleInventoryType type, byte slotLimit) { this.owner = mc; diff --git a/src/client/processor/AssignAPProcessor.java b/src/client/processor/AssignAPProcessor.java new file mode 100644 index 0000000000..603ead6066 --- /dev/null +++ b/src/client/processor/AssignAPProcessor.java @@ -0,0 +1,881 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + Copyleft (L) 2016 - 2018 RonanLana (HeavenMS) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package client.processor; + +import client.MapleCharacter; +import client.MapleClient; +import client.MapleJob; +import client.MapleStat; +import client.Skill; +import client.SkillFactory; +import client.autoban.AutobanFactory; +import client.inventory.Equip; +import client.inventory.Item; +import client.inventory.MapleInventoryType; +import constants.ServerConstants; +import constants.skills.BlazeWizard; +import constants.skills.Brawler; +import constants.skills.DawnWarrior; +import constants.skills.Magician; +import constants.skills.Warrior; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import tools.MaplePacketCreator; +import tools.Pair; +import tools.Randomizer; +import tools.data.input.SeekableLittleEndianAccessor; + +/** + * + * @author RonanLana (synchronization of AP transaction modules) + */ +public class AssignAPProcessor { + + public static void APAutoAssignAction(SeekableLittleEndianAccessor slea, MapleClient c) { + MapleCharacter chr = c.getPlayer(); + if (chr.getRemainingAp() < 1) return; + + Collection equippedC = chr.getInventory(MapleInventoryType.EQUIPPED).list(); + + c.lockClient(); + try { + int[] statGain = new int[4]; + int[] statEqpd = new int[4]; + statGain[0] = 0; statGain[1] = 0; statGain[2] = 0; statGain[3] = 0; + + slea.skip(8); + + if(ServerConstants.USE_SERVER_AUTOASSIGNER) { + // --------- Ronan Lana's AUTOASSIGNER --------- + // This method excels for assigning APs in such a way to cover all equipments AP requirements. + byte opt = slea.readByte(); // useful for pirate autoassigning + + int str = 0, dex = 0, luk = 0, int_ = 0; + List eqpStrList = new ArrayList<>(); + List eqpDexList = new ArrayList<>(); + List eqpLukList = new ArrayList<>(); + + Equip nEquip; + + for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item. + nEquip = (Equip)item; + if(nEquip.getStr() > 0) eqpStrList.add(nEquip.getStr()); + str += nEquip.getStr(); + + if(nEquip.getDex() > 0) eqpDexList.add(nEquip.getDex()); + dex += nEquip.getDex(); + + if(nEquip.getLuk() > 0) eqpLukList.add(nEquip.getLuk()); + luk += nEquip.getLuk(); + + //if(nEquip.getInt() > 0) eqpIntList.add(nEquip.getInt()); //not needed... + int_ += nEquip.getInt(); + } + + statEqpd[0] = str; + statEqpd[1] = dex; + statEqpd[2] = luk; + statEqpd[3] = int_; + + Collections.sort(eqpStrList, Collections.reverseOrder()); + Collections.sort(eqpDexList, Collections.reverseOrder()); + Collections.sort(eqpLukList, Collections.reverseOrder()); + + //Autoassigner looks up the 1st/2nd placed equips for their stats to calculate the optimal upgrade. + int eqpStr = getNthHighestStat(eqpStrList, (short) 0) + getNthHighestStat(eqpStrList, (short) 1); + int eqpDex = getNthHighestStat(eqpDexList, (short) 0) + getNthHighestStat(eqpDexList, (short) 1); + int eqpLuk = getNthHighestStat(eqpLukList, (short) 0) + getNthHighestStat(eqpLukList, (short) 1); + + //c.getPlayer().message("----------------------------------------"); + //c.getPlayer().message("SDL: s" + eqpStr + " d" + eqpDex + " l" + eqpLuk + " BASE STATS --> STR: " + chr.getStr() + " DEX: " + chr.getDex() + " INT: " + chr.getInt() + " LUK: " + chr.getLuk()); + //c.getPlayer().message("SUM EQUIP STATS -> STR: " + str + " DEX: " + dex + " LUK: " + luk + " INT: " + int_); + + MapleJob stance = c.getPlayer().getJobStyle(opt); + int prStat = 0, scStat = 0, trStat = 0, temp, tempAp = chr.getRemainingAp(), CAP; + if (tempAp < 1) return; + + MapleStat primary, secondary, tertiary = MapleStat.LUK; + switch(stance) { + case MAGICIAN: + CAP = 165; + scStat = (chr.getLevel() + 3) - (chr.getLuk() + luk - eqpLuk); + if(scStat < 0) scStat = 0; + scStat = Math.min(scStat, tempAp); + + if(tempAp > scStat) tempAp -= scStat; + else tempAp = 0; + + prStat = tempAp; + int_ = prStat; + luk = scStat; + str = 0; dex = 0; + + if(luk + chr.getLuk() > CAP) { + temp = luk + chr.getLuk() - CAP; + luk -= temp; + int_ += temp; + } + + primary = MapleStat.INT; + secondary = MapleStat.LUK; + tertiary = MapleStat.DEX; + + break; + + case BOWMAN: + CAP = 125; + scStat = (chr.getLevel() + 5) - (chr.getStr() + str - eqpStr); + if(scStat < 0) scStat = 0; + scStat = Math.min(scStat, tempAp); + + if(tempAp > scStat) tempAp -= scStat; + else tempAp = 0; + + prStat = tempAp; + dex = prStat; + str = scStat; + int_ = 0; luk = 0; + + if(str + chr.getStr() > CAP) { + temp = str + chr.getStr() - CAP; + str -= temp; + dex += temp; + } + + primary = MapleStat.DEX; + secondary = MapleStat.STR; + + break; + + case GUNSLINGER: + case CROSSBOWMAN: + CAP = 120; + scStat = chr.getLevel() - (chr.getStr() + str - eqpStr); + if(scStat < 0) scStat = 0; + scStat = Math.min(scStat, tempAp); + + if(tempAp > scStat) tempAp -= scStat; + else tempAp = 0; + + prStat = tempAp; + dex = prStat; + str = scStat; + int_ = 0; luk = 0; + + if(str + chr.getStr() > CAP) { + temp = str + chr.getStr() - CAP; + str -= temp; + dex += temp; + } + + primary = MapleStat.DEX; + secondary = MapleStat.STR; + + break; + + case THIEF: + CAP = 160; + + scStat = 0; + if(chr.getDex() < 80) { + scStat = (2 * chr.getLevel()) - (chr.getDex() + dex - eqpDex); + if(scStat < 0) scStat = 0; + + scStat = Math.min(80 - chr.getDex(), scStat); + scStat = Math.min(tempAp, scStat); + tempAp -= scStat; + } + + temp = (chr.getLevel() + 40) - Math.max(80, scStat + chr.getDex() + dex - eqpDex); + if(temp < 0) temp = 0; + temp = Math.min(tempAp, temp); + scStat += temp; + tempAp -= temp; + + // thieves will upgrade STR as well only if a level-based threshold is reached. + if(chr.getStr() >= Math.max(13, (int)(0.4 * chr.getLevel()))) { + if(chr.getStr() < 50) { + trStat = (chr.getLevel() - 10) - (chr.getStr() + str - eqpStr); + if(trStat < 0) trStat = 0; + + trStat = Math.min(50 - chr.getStr(), trStat); + trStat = Math.min(tempAp, trStat); + tempAp -= trStat; + } + + temp = (20 + (chr.getLevel() / 2)) - Math.max(50, trStat + chr.getStr() + str - eqpStr); + if(temp < 0) temp = 0; + temp = Math.min(tempAp, temp); + trStat += temp; + tempAp -= temp; + } + + prStat = tempAp; + luk = prStat; + dex = scStat; + str = trStat; + int_ = 0; + + if(dex + chr.getDex() > CAP) { + temp = dex + chr.getDex() - CAP; + dex -= temp; + luk += temp; + } + if(str + chr.getStr() > CAP) { + temp = str + chr.getStr() - CAP; + str -= temp; + luk += temp; + } + + primary = MapleStat.LUK; + secondary = MapleStat.DEX; + tertiary = MapleStat.STR; + + break; + + case BRAWLER: + CAP = 120; + + scStat = chr.getLevel() - (chr.getDex() + dex - eqpDex); + if(scStat < 0) scStat = 0; + scStat = Math.min(scStat, tempAp); + + if(tempAp > scStat) tempAp -= scStat; + else tempAp = 0; + + prStat = tempAp; + str = prStat; + dex = scStat; + int_ = 0; luk = 0; + + if(dex + chr.getDex() > CAP) { + temp = dex + chr.getDex() - CAP; + dex -= temp; + str += temp; + } + + primary = MapleStat.STR; + secondary = MapleStat.DEX; + + break; + + default: //warrior, beginner, ... + CAP = 80; + + scStat = ((2 * chr.getLevel()) / 3) - (chr.getDex() + dex - eqpDex); + if(scStat < 0) scStat = 0; + scStat = Math.min(scStat, tempAp); + + if(tempAp > scStat) tempAp -= scStat; + else tempAp = 0; + + prStat = tempAp; + str = prStat; + dex = scStat; + int_ = 0; luk = 0; + + if(dex + chr.getDex() > CAP) { + temp = dex + chr.getDex() - CAP; + dex -= temp; + str += temp; + } + + primary = MapleStat.STR; + secondary = MapleStat.DEX; + } + + //------------------------------------------------------------------------------------- + + int extras = 0; + + extras = gainStatByType(chr, primary, statGain, prStat + extras); + extras = gainStatByType(chr, secondary, statGain, scStat + extras); + extras = gainStatByType(chr, tertiary, statGain, trStat + extras); + + if(extras > 0) { //redistribute surplus in priority order + extras = gainStatByType(chr, primary, statGain, extras); + extras = gainStatByType(chr, secondary, statGain, extras); + extras = gainStatByType(chr, tertiary, statGain, extras); + gainStatByType(chr, getQuaternaryStat(stance), statGain, extras); + } + + int remainingAp = (chr.getRemainingAp() - getAccumulatedStatGain(statGain)); + chr.setRemainingAp(remainingAp); + chr.updateSingleStat(MapleStat.AVAILABLEAP, remainingAp); + c.announce(MaplePacketCreator.enableActions()); + + //---------------------------------------------------------------------------------------- + + c.announce(MaplePacketCreator.serverNotice(1, "Better AP applications detected:\r\nSTR: +" + statGain[0] + "\r\nDEX: +" + statGain[1] + "\r\nINT: +" + statGain[3] + "\r\nLUK: +" + statGain[2])); + } else { + if(slea.available() < 16) { + AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign."); + + final MapleClient client = c; + Thread t = new Thread(new Runnable() { + @Override + public void run() { + client.disconnect(false, false); + } + }); + t.start(); + + return; + } + + for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item. + Equip nEquip = (Equip)item; + + statEqpd[0] += nEquip.getStr(); + statEqpd[1] += nEquip.getDex(); + statEqpd[2] += nEquip.getLuk(); + statEqpd[3] += nEquip.getInt(); + } + + int total = 0; + int extras = 0; + for (int i = 0; i < 2; i++) { + int type = slea.readInt(); + int tempVal = slea.readInt(); + if (tempVal < 0 || tempVal > chr.getRemainingAp()) { + return; + } + total += tempVal; + extras += gainStatByType(chr, MapleStat.getBy5ByteEncoding(type), statGain, tempVal); + } + int remainingAp = (chr.getRemainingAp() - total) + extras; + chr.setRemainingAp(remainingAp); + chr.updateSingleStat(MapleStat.AVAILABLEAP, remainingAp); + c.announce(MaplePacketCreator.enableActions()); + } + } finally { + c.unlockClient(); + } + } + + private static int getNthHighestStat(List statList, short rank) { // ranks from 0 + return(statList.size() <= rank ? 0 : statList.get(rank)); + } + + private static int gainStatByType(MapleCharacter chr, MapleStat type, int[] statGain, int gain) { + if(gain <= 0) return 0; + + int newVal = 0; + if (type.equals(MapleStat.STR)) { + newVal = chr.getStr() + gain; + if (newVal > ServerConstants.MAX_AP) { + statGain[0] += (gain - (newVal - ServerConstants.MAX_AP)); + chr.setStr(ServerConstants.MAX_AP); + } else { + statGain[0] += gain; + chr.setStr(newVal); + } + } else if (type.equals(MapleStat.INT)) { + newVal = chr.getInt() + gain; + if (newVal > ServerConstants.MAX_AP) { + statGain[3] += (gain - (newVal - ServerConstants.MAX_AP)); + chr.setInt(ServerConstants.MAX_AP); + } else { + statGain[3] += gain; + chr.setInt(newVal); + } + } else if (type.equals(MapleStat.LUK)) { + newVal = chr.getLuk() + gain; + if (newVal > ServerConstants.MAX_AP) { + statGain[2] += (gain - (newVal - ServerConstants.MAX_AP)); + chr.setLuk(ServerConstants.MAX_AP); + } else { + statGain[2] += gain; + chr.setLuk(newVal); + } + } else if (type.equals(MapleStat.DEX)) { + newVal = chr.getDex() + gain; + if (newVal > ServerConstants.MAX_AP) { + statGain[1] += (gain - (newVal - ServerConstants.MAX_AP)); + chr.setDex(ServerConstants.MAX_AP); + } else { + statGain[1] += gain; + chr.setDex(newVal); + } + } + + if (newVal > ServerConstants.MAX_AP) { + chr.updateSingleStat(type, ServerConstants.MAX_AP); + return newVal - ServerConstants.MAX_AP; + } + chr.updateSingleStat(type, newVal); + return 0; + } + + private static MapleStat getQuaternaryStat(MapleJob stance) { + if(stance != MapleJob.MAGICIAN) return MapleStat.INT; + return MapleStat.STR; + } + + private static int getAccumulatedStatGain(int[] statGain) { + int acc = 0; + + for(byte i = 0; i < statGain.length; i++) { + acc += statGain[i]; + } + + return acc; + } + + public static boolean APResetAction(MapleClient c, int APFrom, int APTo) { + c.lockClient(); + try { + List> statupdate = new ArrayList<>(2); + MapleCharacter player = c.getPlayer(); + + switch (APFrom) { + case 64: // str + if (player.getStr() < 5) { + player.message("You don't have the minimum STR required to swap."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + player.addStat(1, -1); + break; + case 128: // dex + if (player.getDex() < 5) { + player.message("You don't have the minimum DEX required to swap."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + player.addStat(2, -1); + break; + case 256: // int + if (player.getInt() < 5) { + player.message("You don't have the minimum INT required to swap."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + player.addStat(3, -1); + break; + case 512: // luk + if (player.getLuk() < 5) { + player.message("You don't have the minimum LUK required to swap."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + player.addStat(4, -1); + break; + case 2048: // HP + if(ServerConstants.USE_ENFORCE_HPMP_SWAP) { + if (APTo != 8192) { + player.message("You can only swap HP ability points to MP."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + } + if (player.getHpMpApUsed() < 1) { + player.message("You don't have enough HPMP stat points to spend on AP Reset."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + + int hp = player.getMaxHp(); + int level_ = player.getLevel(); + + boolean canWash_ = true; + if (hp < level_ * 14 + 148) { + canWash_ = false; + } + + if (!canWash_) { + player.message("You don't have the minimum HP pool required to swap."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + + player.setHpMpApUsed(player.getHpMpApUsed() - 1); + int hplose = -takeHp(player.getJob()); + int nextHp = Math.max(1, player.getHp() + hplose), nextMaxHp = Math.max(50, player.getMaxHp() + hplose); + + player.setHp(nextHp); + player.setMaxHp(nextMaxHp); + statupdate.add(new Pair<>(MapleStat.HP, nextHp)); + statupdate.add(new Pair<>(MapleStat.MAXHP, nextMaxHp)); + + break; + case 8192: // MP + if(ServerConstants.USE_ENFORCE_HPMP_SWAP) { + if (APTo != 2048) { + player.message("You can only swap MP ability points to HP."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + } + if (player.getHpMpApUsed() < 1) { + player.message("You don't have enough HPMP stat points to spend on AP Reset."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + + int mp = player.getMaxMp(); + int level = player.getLevel(); + MapleJob job = player.getJob(); + + boolean canWash = true; + if (job.isA(MapleJob.SPEARMAN) && mp < 4 * level + 156) { + canWash = false; + } else if (job.isA(MapleJob.FIGHTER) && mp < 4 * level + 56) { + canWash = false; + } else if (job.isA(MapleJob.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) { + canWash = false; + } else if (mp < level * 14 + 148) { + canWash = false; + } + + if (!canWash) { + player.message("You don't have the minimum MP pool required to swap."); + c.announce(MaplePacketCreator.enableActions()); + return false; + } + + player.setHpMpApUsed(player.getHpMpApUsed() - 1); + int mplose = -takeMp(job); + int nextMp = Math.max(0, player.getMp() + mplose), nextMaxMp = Math.max(5, player.getMaxMp() + mplose); + + player.setMp(nextMp); + player.setMaxMp(nextMaxMp); + statupdate.add(new Pair<>(MapleStat.MP, nextMp)); + statupdate.add(new Pair<>(MapleStat.MAXMP, nextMaxMp)); + + break; + default: + c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, player)); + return false; + } + + addStat(c, APTo, true); + c.announce(MaplePacketCreator.updatePlayerStats(statupdate, true, player)); + return true; + } finally { + c.unlockClient(); + } + } + + public static void APAssignAction(MapleClient c, int num) { + c.lockClient(); + try { + if (c.getPlayer().getRemainingAp() > 0) { + if (addStat(c, num, false)) { + c.getPlayer().setRemainingAp(c.getPlayer().getRemainingAp() - 1); + c.getPlayer().updateSingleStat(MapleStat.AVAILABLEAP, c.getPlayer().getRemainingAp()); + } + } + c.announce(MaplePacketCreator.enableActions()); + } finally { + c.unlockClient(); + } + } + + private static boolean addStat(MapleClient c, int apTo, boolean usedAPReset) { + switch (apTo) { + case 64: // Str + if (c.getPlayer().getStr() >= 32767) { + return false; + } + c.getPlayer().addStat(1, 1); + break; + case 128: // Dex + if (c.getPlayer().getDex() >= 32767) { + return false; + } + c.getPlayer().addStat(2, 1); + break; + case 256: // Int + if (c.getPlayer().getInt() >= 32767) { + return false; + } + c.getPlayer().addStat(3, 1); + break; + case 512: // Luk + if (c.getPlayer().getLuk() >= 32767) { + return false; + } + c.getPlayer().addStat(4, 1); + break; + case 2048: // HP + addHP(c.getPlayer(), addHP(c, usedAPReset)); + break; + case 8192: // MP + addMP(c.getPlayer(), addMP(c, usedAPReset)); + break; + default: + c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, c.getPlayer())); + return false; + } + return true; + } + + private static int addHP(MapleClient c, boolean usedAPReset) { + MapleCharacter player = c.getPlayer(); + MapleJob job = player.getJob(); + int MaxHP = player.getMaxHp(); + if (player.getHpMpApUsed() > 9999 || MaxHP >= 30000) { + return MaxHP; + } + + return MaxHP + calcHpChange(player, job, usedAPReset); + } + + private static int calcHpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) { + int MaxHP = 0; + + if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) { + if(!usedAPReset) { + Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.DAWNWARRIOR1) ? DawnWarrior.MAX_HP_INCREASE : Warrior.IMPROVED_MAXHP); + int sLvl = player.getSkillLevel(increaseHP); + + if(sLvl > 0) + MaxHP += increaseHP.getEffect(sLvl).getY(); + } + + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if (usedAPReset) { + MaxHP += 20; + } else { + MaxHP += Randomizer.rand(18, 22); + } + } else { + MaxHP += 20; + } + } else if(job.isA(MapleJob.ARAN1)) { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if (usedAPReset) { + MaxHP += 20; + } else { + MaxHP += Randomizer.rand(26, 30); + } + } else { + MaxHP += 28; + } + } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if (usedAPReset) { + MaxHP += 6; + } else { + MaxHP += Randomizer.rand(5, 9); + } + } else { + MaxHP += 6; + } + } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if (usedAPReset) { + MaxHP += 16; + } else { + MaxHP += Randomizer.rand(14, 18); + } + } else { + MaxHP += 16; + } + } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if (usedAPReset) { + MaxHP += 16; + } else { + MaxHP += Randomizer.rand(14, 18); + } + } else { + MaxHP += 16; + } + } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { + if(!usedAPReset) { + Skill increaseHP = SkillFactory.getSkill(Brawler.IMPROVE_MAX_HP); + int sLvl = player.getSkillLevel(increaseHP); + + if(sLvl > 0) + MaxHP += increaseHP.getEffect(sLvl).getY(); + } + + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if (usedAPReset) { + MaxHP += 18; + } else { + MaxHP += Randomizer.rand(16, 20); + } + } else { + MaxHP += 18; + } + } else if (usedAPReset) { + MaxHP += 8; + } else { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + MaxHP += Randomizer.rand(8, 12); + } else { + MaxHP += 10; + } + } + + return MaxHP; + } + + private static int addMP(MapleClient c, boolean usedAPReset) { + MapleCharacter player = c.getPlayer(); + int MaxMP = player.getMaxMp(); + MapleJob job = player.getJob(); + if (player.getHpMpApUsed() > 9999 || player.getMaxMp() >= 30000) { + return MaxMP; + } + + return MaxMP + calcMpChange(player, job, usedAPReset); + } + + private static int calcMpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) { + int MaxMP = 0; + + if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(!usedAPReset) { + MaxMP += (Randomizer.rand(2, 4) + (player.getInt() / 10)); + } else { + MaxMP += 2; + } + } else { + MaxMP += 3; + } + } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { + if(!usedAPReset) { + Skill increaseMP = SkillFactory.getSkill(job.isA(MapleJob.BLAZEWIZARD1) ? BlazeWizard.INCREASING_MAX_MP : Magician.IMPROVED_MAX_MP_INCREASE); + int sLvl = player.getSkillLevel(increaseMP); + + if(sLvl > 0) + MaxMP += increaseMP.getEffect(sLvl).getY(); + } + + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(!usedAPReset) { + MaxMP += (Randomizer.rand(12, 16) + (player.getInt() / 20)); + } else { + MaxMP += 18; + } + } else { + MaxMP += 18; + } + } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(!usedAPReset) { + MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10)); + } else { + MaxMP += 10; + } + } else { + MaxMP += 10; + } + } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(!usedAPReset) { + MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10)); + } else { + MaxMP += 10; + } + } else { + MaxMP += 10; + } + } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(!usedAPReset) { + MaxMP += (Randomizer.rand(7, 9) + (player.getInt() / 10)); + } else { + MaxMP += 14; + } + } else { + MaxMP += 14; + } + } else { + if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { + if(!usedAPReset) { + MaxMP += (Randomizer.rand(4, 6) + (player.getInt() / 10)); + } else { + MaxMP += 6; + } + } else { + MaxMP += 6; + } + } + + return MaxMP; + } + + private static void addHP(MapleCharacter player, int MaxHP) { + MaxHP = Math.min(30000, MaxHP); + player.setHpMpApUsed(player.getHpMpApUsed() + 1); + player.setMaxHp(MaxHP); + player.updateSingleStat(MapleStat.MAXHP, MaxHP); + } + + private static void addMP(MapleCharacter player, int MaxMP) { + MaxMP = Math.min(30000, MaxMP); + player.setHpMpApUsed(player.getHpMpApUsed() + 1); + player.setMaxMp(MaxMP); + player.updateSingleStat(MapleStat.MAXMP, MaxMP); + } + + private static int takeHp(MapleJob job) { + int MaxHP = 0; + + if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) { + MaxHP += 54; + } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { + MaxHP += 10; + } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { + MaxHP += 20; + } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { + MaxHP += 20; + } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { + MaxHP += 42; + } else { + MaxHP += 12; + } + + return MaxHP; + } + + private static int takeMp(MapleJob job) { + int MaxMP = 0; + + if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) { + MaxMP += 4; + } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { + MaxMP += 31; + } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { + MaxMP += 12; + } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { + MaxMP += 12; + } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { + MaxMP += 16; + } else { + MaxMP += 8; + } + + return MaxMP; + } + +} diff --git a/src/client/processor/AssignSPProcessor.java b/src/client/processor/AssignSPProcessor.java new file mode 100644 index 0000000000..35fcd30250 --- /dev/null +++ b/src/client/processor/AssignSPProcessor.java @@ -0,0 +1,100 @@ +/* + This file is part of the OdinMS Maple Story Server + Copyright (C) 2008 Patrick Huy + Matthias Butz + Jan Christian Meyer + + Copyleft (L) 2016 - 2018 RonanLana (HeavenMS) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package client.processor; + +import client.MapleCharacter; +import client.MapleClient; +import client.MapleStat; +import client.Skill; +import client.SkillFactory; +import client.autoban.AutobanFactory; +import constants.GameConstants; +import constants.skills.Aran; +import tools.FilePrinter; +import tools.MaplePacketCreator; + +/** + * + * @author RonanLana (synchronization of SP transaction modules) + */ +public class AssignSPProcessor { + + public static void SPAssignAction(MapleClient c, int skillid) { + c.lockClient(); + try { + if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) { + c.getSession().write(MaplePacketCreator.enableActions()); + return; + } + + MapleCharacter player = c.getPlayer(); + int remainingSp = player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)); + boolean isBeginnerSkill = false; + if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) { + AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp."); + FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n"); + + final MapleClient client = c; + Thread t = new Thread(new Runnable() { + @Override + public void run() { + client.disconnect(true, false); + } + }); + t.start(); + + return; + } + if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) { + int total = 0; + for (int i = 0; i < 3; i++) { + total += player.getSkillLevel(SkillFactory.getSkill(player.getJobType() * 10000000 + 1000 + i)); + } + remainingSp = Math.min((player.getLevel() - 1), 6) - total; + isBeginnerSkill = true; + } + Skill skill = SkillFactory.getSkill(skillid); + int curLevel = player.getSkillLevel(skill); + if ((remainingSp > 0 && curLevel + 1 <= (skill.isFourthJob() ? player.getMasterLevel(skill) : skill.getMaxLevel()))) { + if (!isBeginnerSkill) { + player.setRemainingSp(player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)) - 1, GameConstants.getSkillBook(skillid/10000)); + } + player.updateSingleStat(MapleStat.AVAILABLESP, player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000))); + if (skill.getId() == Aran.FULL_SWING) { + player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill)); + player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill)); + player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill)); + } else if (skill.getId() == Aran.OVER_SWING) { + player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill)); + player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill)); + player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill)); + } else { + player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill)); + } + } + } finally { + c.unlockClient(); + } + } +} diff --git a/src/client/processor/DueyProcessor.java b/src/client/processor/DueyProcessor.java index 6db886ffd8..3a03da5e77 100644 --- a/src/client/processor/DueyProcessor.java +++ b/src/client/processor/DueyProcessor.java @@ -149,6 +149,8 @@ public class DueyProcessor { Equip eq = new Equip(rs.getInt("itemid"), (byte) 0, -1); eq.setUpgradeSlots((byte) rs.getInt("upgradeslots")); eq.setLevel((byte) rs.getInt("level")); + eq.setItemLevel((byte) rs.getInt("itemlevel")); + eq.setItemExp(rs.getInt("itemexp")); eq.setStr((short) rs.getInt("str")); eq.setDex((short) rs.getInt("dex")); eq.setInt((short) rs.getInt("int")); @@ -271,29 +273,31 @@ public class DueyProcessor { rs.next(); PreparedStatement ps2; if (item.getInventoryType().equals(MapleInventoryType.EQUIP)) { - ps2 = con.prepareStatement("INSERT INTO dueyitems (PackageId, itemid, quantity, upgradeslots, level, str, dex, `int`, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, flag, owner) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); + ps2 = con.prepareStatement("INSERT INTO dueyitems (PackageId, itemid, quantity, upgradeslots, level, itemlevel, itemexp, str, dex, `int`, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, flag, owner) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"); Equip eq = (Equip) item; ps2.setInt(2, eq.getItemId()); ps2.setInt(3, 1); ps2.setInt(4, eq.getUpgradeSlots()); ps2.setInt(5, eq.getLevel()); - ps2.setInt(6, eq.getStr()); - ps2.setInt(7, eq.getDex()); - ps2.setInt(8, eq.getInt()); - ps2.setInt(9, eq.getLuk()); - ps2.setInt(10, eq.getHp()); - ps2.setInt(11, eq.getMp()); - ps2.setInt(12, eq.getWatk()); - ps2.setInt(13, eq.getMatk()); - ps2.setInt(14, eq.getWdef()); - ps2.setInt(15, eq.getMdef()); - ps2.setInt(16, eq.getAcc()); - ps2.setInt(17, eq.getAvoid()); - ps2.setInt(18, eq.getHands()); - ps2.setInt(19, eq.getSpeed()); - ps2.setInt(20, eq.getJump()); - ps2.setInt(21, eq.getFlag()); - ps2.setString(22, eq.getOwner()); + ps2.setInt(6, eq.getItemLevel()); + ps2.setInt(7, eq.getItemExp()); + ps2.setInt(8, eq.getStr()); + ps2.setInt(9, eq.getDex()); + ps2.setInt(10, eq.getInt()); + ps2.setInt(11, eq.getLuk()); + ps2.setInt(12, eq.getHp()); + ps2.setInt(13, eq.getMp()); + ps2.setInt(14, eq.getWatk()); + ps2.setInt(15, eq.getMatk()); + ps2.setInt(16, eq.getWdef()); + ps2.setInt(17, eq.getMdef()); + ps2.setInt(18, eq.getAcc()); + ps2.setInt(19, eq.getAvoid()); + ps2.setInt(20, eq.getHands()); + ps2.setInt(21, eq.getSpeed()); + ps2.setInt(22, eq.getJump()); + ps2.setInt(23, eq.getFlag()); + ps2.setString(24, eq.getOwner()); } else { ps2 = con.prepareStatement("INSERT INTO dueyitems (PackageId, itemid, quantity, flag, owner) VALUES (?, ?, ?, ?, ?)"); ps2.setInt(2, item.getItemId()); diff --git a/src/constants/ServerConstants.java b/src/constants/ServerConstants.java index 1a952fe7d3..61f686c598 100644 --- a/src/constants/ServerConstants.java +++ b/src/constants/ServerConstants.java @@ -46,8 +46,8 @@ public class ServerConstants { public static final boolean USE_DEBUG_SHOW_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info. public static boolean USE_DEBUG_SHOW_RCVD_PACKET = false; //Prints on the cmd all received packet ids. - public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true; public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events on a map, rather than those of only view range. + public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true; public static final boolean USE_MTS = false; public static final boolean USE_AUTOHIDE_GM = false; //When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152). public static final boolean USE_BUYBACK_SYSTEM = true; //Enables the HeavenMS-builtin buyback system, to be used by dead players when clicking the MTS button. @@ -80,6 +80,8 @@ public class ServerConstants { public static final boolean USE_BANISHABLE_TOWN_SCROLL = true; //Enables town scrolls to act as if it's a "player banish", rendering the antibanish scroll effect available. public static final boolean USE_OLD_GMS_STYLED_PQ_NPCS = true; //Enables PQ NPCs with similar behaviour to old GMS style, that skips info about the PQs and immediately tries to register the party in. public static final boolean USE_ENABLE_SOLO_EXPEDITIONS = true; //Enables start expeditions with any number of players. This will also bypass all the Zakum prequest. + public static final boolean USE_ENABLE_FULL_RESPAWN = true; //At respawn task, always respawn missing mobs when they're available. Spawn count doesn't depend on how many players are currently there. + public static final boolean USE_SPAWN_LOOT_ON_ANIMATION = false;//Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly). //Announcement Configuration public static final boolean USE_ANNOUNCE_SHOPITEMSOLD = false; //Automatic message sent to owner when an item from the Player Shop or Hired Merchant is sold. @@ -122,10 +124,12 @@ public class ServerConstants { public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes. public static final int KITE_EXPIRE_TIME = 60 * 60 * 1000; //Time before kites (cash item) disappears. public static final int ITEM_MONITOR_TIME = 5 * 60 * 1000; //Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history. - public static final int LOCK_MONITOR_TIME = 3 * 60 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present. + public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present. + + //Map Monitor Configuration public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items. public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map. - public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions. + public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids visited by a player. This is used to recover and update drops on these maps accordingly with player actions. //Channel Mob Disease Monitor Configuration public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule. diff --git a/src/net/MapleServerHandler.java b/src/net/MapleServerHandler.java index aa5409c5eb..16eeac4ce8 100644 --- a/src/net/MapleServerHandler.java +++ b/src/net/MapleServerHandler.java @@ -29,7 +29,7 @@ import java.util.Calendar; import java.util.concurrent.atomic.AtomicLong; import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.Server; import org.apache.mina.core.service.IoHandlerAdapter; @@ -63,8 +63,8 @@ public class MapleServerHandler extends IoHandlerAdapter { private static final SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm"); private static AtomicLong sessionId = new AtomicLong(7777); - private Lock idleLock = new MonitoredReentrantLock(MonitoredLockType.SRVHANDLER_IDLE, true); - private Lock tempLock = new MonitoredReentrantLock(MonitoredLockType.SRVHANDLER_TEMP, true); + private Lock idleLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SRVHANDLER_IDLE, true); + private Lock tempLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SRVHANDLER_TEMP, true); private Map idleSessions = new HashMap<>(100); private Map tempIdleSessions = new HashMap<>(); private ScheduledFuture idleManager = null; diff --git a/src/net/opcodes/SendOpcode.java b/src/net/opcodes/SendOpcode.java index 193c761d92..2786d6f7a4 100644 --- a/src/net/opcodes/SendOpcode.java +++ b/src/net/opcodes/SendOpcode.java @@ -169,7 +169,7 @@ public enum SendOpcode { FIELD_EFFECT(0x8A), FIELD_OBSTACLE_ONOFF(0x8B), - FIELD_OBSTACLE_ONOFF_STATUS(0x8C), + FIELD_OBSTACLE_ONOFF_LIST(0x8C), FIELD_OBSTACLE_ALL_RESET(0x8D), BLOW_WEATHER(0x8E), PLAY_JUKEBOX(0x8F), diff --git a/src/net/server/PlayerBuffStorage.java b/src/net/server/PlayerBuffStorage.java index 5efb6029e7..c936c98898 100644 --- a/src/net/server/PlayerBuffStorage.java +++ b/src/net/server/PlayerBuffStorage.java @@ -29,7 +29,7 @@ import java.util.concurrent.locks.Lock; import server.life.MobSkill; import tools.Pair; import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -38,7 +38,7 @@ import net.server.audit.locks.MonitoredReentrantLock; */ public class PlayerBuffStorage { private int id = (int) (Math.random() * 100); - private final Lock lock = new MonitoredReentrantLock(MonitoredLockType.BUFF_STORAGE, true); + private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.BUFF_STORAGE, true); private Map> buffs = new HashMap<>(); private Map>> diseases = new HashMap<>(); diff --git a/src/net/server/PlayerStorage.java b/src/net/server/PlayerStorage.java index ae6d595429..9bd6d1647d 100644 --- a/src/net/server/PlayerStorage.java +++ b/src/net/server/PlayerStorage.java @@ -23,8 +23,9 @@ package net.server; import client.MapleClient; import client.MapleCharacter; +import java.util.ArrayList; import java.util.Collection; -import java.util.Iterator; +import java.util.List; import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; @@ -35,10 +36,10 @@ import net.server.audit.locks.MonitoredReentrantReadWriteLock; public class PlayerStorage { private final ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.PLAYER_STORAGE, true); - private final ReadLock rlock = locks.readLock(); - private final WriteLock wlock = locks.writeLock(); private final Map storage = new LinkedHashMap<>(); private final Map nameStorage = new LinkedHashMap<>(); + private ReadLock rlock = locks.readLock(); + private WriteLock wlock = locks.writeLock(); public void addPlayer(MapleCharacter chr) { wlock.lock(); @@ -90,17 +91,24 @@ public class PlayerStorage { } public final void disconnectAll() { - wlock.lock(); - try { - final Iterator chrit = storage.values().iterator(); - while (chrit.hasNext()) { - MapleClient client = chrit.next().getClient(); - if(client != null) { - client.disconnect(true, false); - } - - chrit.remove(); + List chrList; + rlock.lock(); + try { + chrList = new ArrayList<>(storage.values()); + } finally { + rlock.unlock(); + } + + for(MapleCharacter mc : chrList) { + MapleClient client = mc.getClient(); + if(client != null) { + client.disconnect(true, false); } + } + + wlock.lock(); + try { + storage.clear(); } finally { wlock.unlock(); } diff --git a/src/net/server/Server.java b/src/net/server/Server.java index 088761dffd..45d189aca4 100644 --- a/src/net/server/Server.java +++ b/src/net/server/Server.java @@ -48,8 +48,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.ThreadTracker; import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.MapleServerHandler; import net.mina.MapleCodecFactory; @@ -111,8 +111,8 @@ public class Server { private final List processDiseaseAnnouncePlayers = new LinkedList<>(); private final List registeredDiseaseAnnouncePlayers = new LinkedList<>(); - private final Lock srvLock = new MonitoredReentrantLock(MonitoredLockType.SERVER); - private final Lock disLock = new MonitoredReentrantLock(MonitoredLockType.SERVER_DISEASES); + private final Lock srvLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER); + private final Lock disLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.SERVER_DISEASES); private final ReentrantReadWriteLock wldLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.SERVER_WORLDS, true); private final ReadLock wldRLock = wldLock.readLock(); @@ -412,7 +412,7 @@ public class Server { wldRLock.unlock(); } - if(w == null || w.getPlayerStorage().getSize() > 0) { + if(w == null || !w.canUninstall()) { return false; } @@ -1414,12 +1414,11 @@ public class Server { } }*/ + List allChannels = getAllChannels(); + if(ServerConstants.USE_THREAD_TRACKER) ThreadTracker.getInstance().cancelThreadTrackerTask(); - - TimerManager.getInstance().purge(); - TimerManager.getInstance().stop(); - - for (Channel ch : getAllChannels()) { + + for (Channel ch : allChannels) { while (!ch.finishedShutdown()) { try { Thread.sleep(1000); @@ -1431,6 +1430,9 @@ public class Server { } resetServerWorlds(); + + TimerManager.getInstance().purge(); + TimerManager.getInstance().stop(); System.out.println("Worlds + Channels are offline."); acceptor.unbind(); diff --git a/src/net/server/audit/ThreadTracker.java b/src/net/server/audit/ThreadTracker.java index 3566d2ed71..dc916553c4 100644 --- a/src/net/server/audit/ThreadTracker.java +++ b/src/net/server/audit/ThreadTracker.java @@ -142,12 +142,15 @@ public class ThreadTracker { if(tt.isEmpty()) { toRemove.add(l); } else { - DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); - String df = dateFormat.format(new Date()); - - FilePrinter.print(FilePrinter.DEADLOCK_LOCKS, printThreadLog(tt, df)); - FilePrinter.print(FilePrinter.DEADLOCK_STACK, printThreadStack(threads.get(l).getStackTrace(), df)); + StackTraceElement[] ste = threads.get(l).getStackTrace(); + if(ste.length > 0) { + DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + String df = dateFormat.format(new Date()); + + FilePrinter.print(FilePrinter.DEADLOCK_LOCKS, printThreadLog(tt, df)); + FilePrinter.print(FilePrinter.DEADLOCK_STACK, printThreadStack(ste, df)); + } } } diff --git a/src/net/server/audit/locks/MonitoredLockType.java b/src/net/server/audit/locks/MonitoredLockType.java index 5397fadfee..ad26cea77b 100644 --- a/src/net/server/audit/locks/MonitoredLockType.java +++ b/src/net/server/audit/locks/MonitoredLockType.java @@ -87,6 +87,7 @@ public enum MonitoredLockType { MAP_OBJS, MAP_FACTORY, MAP_ITEM, + MAP_LOOT, MAP_BOUNDS, MINIDUNGEON, REACTOR, diff --git a/src/net/server/audit/locks/MonitoredReadLock.java b/src/net/server/audit/locks/MonitoredReadLock.java index dafe2286c5..b60599dd70 100644 --- a/src/net/server/audit/locks/MonitoredReadLock.java +++ b/src/net/server/audit/locks/MonitoredReadLock.java @@ -1,6 +1,6 @@ /* This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2018 RonanLana + Copyleft 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 @@ -19,143 +19,18 @@ */ package net.server.audit.locks; -import constants.ServerConstants; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.ScheduledFuture; -import server.TimerManager; -import net.server.Server; -import net.server.audit.ThreadTracker; - -import tools.FilePrinter; - /** * * @author RonanLana */ -public class MonitoredReadLock extends ReentrantReadWriteLock.ReadLock { - private ScheduledFuture timeoutSchedule = null; - private StackTraceElement[] deadlockedState = null; - private final MonitoredLockType id; - private final int hashcode; - private final Lock state = new ReentrantLock(true); - private final AtomicInteger reentrantCount = new AtomicInteger(0); +public interface MonitoredReadLock { - public MonitoredReadLock(MonitoredReentrantReadWriteLock lock) { - super(lock); - this.id = lock.id; - hashcode = this.hashCode(); - } + public void lock(); - @Override - public void lock() { - if(ServerConstants.USE_THREAD_TRACKER) { - if(deadlockedState != null) { - DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); - - //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); - ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); - deadlockedState = null; - } - - registerLocking(); - } - - super.lock(); - } + public void unlock(); - @Override - public void unlock() { - if(ServerConstants.USE_THREAD_TRACKER) { - unregisterLocking(); - } - - super.unlock(); - } + public boolean tryLock(); - @Override - public boolean tryLock() { - if(super.tryLock()) { - if(ServerConstants.USE_THREAD_TRACKER) { - if(deadlockedState != null) { - //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); - ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); - deadlockedState = null; - } - - registerLocking(); - } - return true; - } else { - return false; - } - } + public MonitoredReadLock dispose(); - private void registerLocking() { - state.lock(); - try { - ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode); - - if(reentrantCount.incrementAndGet() == 1) { - final Thread t = Thread.currentThread(); - timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() { - @Override - public void run() { - issueDeadlock(t); - } - }, ServerConstants.LOCK_MONITOR_TIME); - } - } finally { - state.unlock(); - } - } - - private void unregisterLocking() { - state.lock(); - try { - if(reentrantCount.decrementAndGet() == 0) { - if(timeoutSchedule != null) { - timeoutSchedule.cancel(false); - timeoutSchedule = null; - } - } - - ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode); - } finally { - state.unlock(); - } - } - - private void issueDeadlock(Thread t) { - deadlockedState = t.getStackTrace(); - //super.unlock(); - } - - private static String printStackTrace(StackTraceElement[] list) { - String s = ""; - for(int i = 0; i < list.length; i++) { - s += (" " + list[i].toString() + "\r\n"); - } - - return s; - } - - public void dispose() { - state.lock(); - try { - if(timeoutSchedule != null) { - timeoutSchedule.cancel(false); - timeoutSchedule = null; - } - } finally { - state.unlock(); - } - } } diff --git a/src/net/server/audit/locks/MonitoredReentrantLock.java b/src/net/server/audit/locks/MonitoredReentrantLock.java index a0ae846af4..ad8af31fe7 100644 --- a/src/net/server/audit/locks/MonitoredReentrantLock.java +++ b/src/net/server/audit/locks/MonitoredReentrantLock.java @@ -1,6 +1,6 @@ /* This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2018 RonanLana + Copyleft 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 @@ -19,147 +19,18 @@ */ package net.server.audit.locks; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.ScheduledFuture; -import constants.ServerConstants; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; -import server.TimerManager; -import net.server.Server; -import net.server.audit.ThreadTracker; -import tools.FilePrinter; - /** * * @author RonanLana */ -public class MonitoredReentrantLock extends ReentrantLock { - private ScheduledFuture timeoutSchedule = null; - private StackTraceElement[] deadlockedState = null; - private final MonitoredLockType id; - private final int hashcode; - private final Lock state = new ReentrantLock(true); - private final AtomicInteger reentrantCount = new AtomicInteger(0); - - public MonitoredReentrantLock(MonitoredLockType id) { - super(); - this.id = id; - hashcode = this.hashCode(); - } - - public MonitoredReentrantLock(MonitoredLockType id, boolean fair) { - super(fair); - this.id = id; - hashcode = this.hashCode(); - } +public interface MonitoredReentrantLock { - @Override - public void lock() { - if(ServerConstants.USE_THREAD_TRACKER) { - if(deadlockedState != null) { - DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); - - //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); - ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); - deadlockedState = null; - } - - registerLocking(); - } - - super.lock(); - } + public void lock(); - @Override - public void unlock() { - if(ServerConstants.USE_THREAD_TRACKER) { - unregisterLocking(); - } - - super.unlock(); - } + public void unlock(); - @Override - public boolean tryLock() { - if(super.tryLock()) { - if(ServerConstants.USE_THREAD_TRACKER) { - if(deadlockedState != null) { - //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); - ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); - deadlockedState = null; - } - - registerLocking(); - } - return true; - } else { - return false; - } - } + public boolean tryLock(); - private void registerLocking() { - state.lock(); - try { - ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode); - - if(reentrantCount.incrementAndGet() == 1) { - final Thread t = Thread.currentThread(); - timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() { - @Override - public void run() { - issueDeadlock(t); - } - }, ServerConstants.LOCK_MONITOR_TIME); - } - } finally { - state.unlock(); - } - } + public MonitoredReentrantLock dispose(); - private void unregisterLocking() { - state.lock(); - try { - if(reentrantCount.decrementAndGet() == 0) { - if(timeoutSchedule != null) { - timeoutSchedule.cancel(false); - timeoutSchedule = null; - } - } - - ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode); - } finally { - state.unlock(); - } - } - - private void issueDeadlock(Thread t) { - deadlockedState = t.getStackTrace(); - //super.unlock(); - } - - private static String printStackTrace(StackTraceElement[] list) { - String s = ""; - for(int i = 0; i < list.length; i++) { - s += (" " + list[i].toString() + "\r\n"); - } - - return s; - } - - public void dispose() { - state.lock(); - try { - if(timeoutSchedule != null) { - timeoutSchedule.cancel(false); - timeoutSchedule = null; - } - } finally { - state.unlock(); - } - } } diff --git a/src/net/server/audit/locks/MonitoredWriteLock.java b/src/net/server/audit/locks/MonitoredWriteLock.java index e79db659bd..bef3ef3ba6 100644 --- a/src/net/server/audit/locks/MonitoredWriteLock.java +++ b/src/net/server/audit/locks/MonitoredWriteLock.java @@ -1,6 +1,6 @@ /* This file is part of the HeavenMS MapleStory Server - Copyleft (L) 2016 - 2018 RonanLana + Copyleft 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 @@ -19,142 +19,18 @@ */ package net.server.audit.locks; -import constants.ServerConstants; -import java.text.DateFormat; -import java.text.SimpleDateFormat; -import java.util.Date; -import java.util.TimeZone; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.Lock; -import java.util.concurrent.locks.ReentrantLock; -import java.util.concurrent.ScheduledFuture; -import server.TimerManager; -import net.server.Server; -import net.server.audit.ThreadTracker; -import tools.FilePrinter; - /** * * @author RonanLana */ -public class MonitoredWriteLock extends ReentrantReadWriteLock.WriteLock { - private ScheduledFuture timeoutSchedule = null; - private StackTraceElement[] deadlockedState = null; - private final MonitoredLockType id; - private final int hashcode; - private final Lock state = new ReentrantLock(true); - private final AtomicInteger reentrantCount = new AtomicInteger(0); - - public MonitoredWriteLock(MonitoredReentrantReadWriteLock lock) { - super(lock); - this.id = lock.id; - hashcode = this.hashCode(); - } +public interface MonitoredWriteLock { - @Override - public void lock() { - if(ServerConstants.USE_THREAD_TRACKER) { - if(deadlockedState != null) { - DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); - dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); - - //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); - ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); - deadlockedState = null; - } - - registerLocking(); - } - - super.lock(); - } + public void lock(); - @Override - public void unlock() { - if(ServerConstants.USE_THREAD_TRACKER) { - unregisterLocking(); - } - - super.unlock(); - } + public void unlock(); - @Override - public boolean tryLock() { - if(super.tryLock()) { - if(ServerConstants.USE_THREAD_TRACKER) { - if(deadlockedState != null) { - //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); - ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); - deadlockedState = null; - } - - registerLocking(); - } - return true; - } else { - return false; - } - } + public boolean tryLock(); - private void registerLocking() { - state.lock(); - try { - ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode); - - if(reentrantCount.incrementAndGet() == 1) { - final Thread t = Thread.currentThread(); - timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() { - @Override - public void run() { - issueDeadlock(t); - } - }, ServerConstants.LOCK_MONITOR_TIME); - } - } finally { - state.unlock(); - } - } + public MonitoredWriteLock dispose(); - private void unregisterLocking() { - state.lock(); - try { - if(reentrantCount.decrementAndGet() == 0) { - if(timeoutSchedule != null) { - timeoutSchedule.cancel(false); - timeoutSchedule = null; - } - } - - ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode); - } finally { - state.unlock(); - } - } - - private void issueDeadlock(Thread t) { - deadlockedState = t.getStackTrace(); - //super.unlock(); - } - - private static String printStackTrace(StackTraceElement[] list) { - String s = ""; - for(int i = 0; i < list.length; i++) { - s += (" " + list[i].toString() + "\r\n"); - } - - return s; - } - - public void dispose() { - state.lock(); - try { - if(timeoutSchedule != null) { - timeoutSchedule.cancel(false); - timeoutSchedule = null; - } - } finally { - state.unlock(); - } - } } diff --git a/src/net/server/audit/locks/active/TrackerReadLock.java b/src/net/server/audit/locks/active/TrackerReadLock.java new file mode 100644 index 0000000000..77f1723480 --- /dev/null +++ b/src/net/server/audit/locks/active/TrackerReadLock.java @@ -0,0 +1,169 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.active; + +import constants.ServerConstants; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.ScheduledFuture; +import server.TimerManager; + +import net.server.audit.ThreadTracker; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReadLock; +import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.empty.EmptyReadLock; + +import tools.FilePrinter; + +/** + * + * @author RonanLana + */ +public class TrackerReadLock extends ReentrantReadWriteLock.ReadLock implements MonitoredReadLock { + private ScheduledFuture timeoutSchedule = null; + private StackTraceElement[] deadlockedState = null; + private final MonitoredLockType id; + private final int hashcode; + private final Lock state = new ReentrantLock(true); + private final AtomicInteger reentrantCount = new AtomicInteger(0); + + public TrackerReadLock(MonitoredReentrantReadWriteLock lock) { + super(lock); + this.id = lock.id; + hashcode = this.hashCode(); + } + + @Override + public void lock() { + if(ServerConstants.USE_THREAD_TRACKER) { + if(deadlockedState != null) { + DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + + //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); + ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); + deadlockedState = null; + } + + registerLocking(); + } + + super.lock(); + } + + @Override + public void unlock() { + if(ServerConstants.USE_THREAD_TRACKER) { + unregisterLocking(); + } + + super.unlock(); + } + + @Override + public boolean tryLock() { + if(super.tryLock()) { + if(ServerConstants.USE_THREAD_TRACKER) { + if(deadlockedState != null) { + //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); + ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); + deadlockedState = null; + } + + registerLocking(); + } + return true; + } else { + return false; + } + } + + private void registerLocking() { + state.lock(); + try { + ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode); + + if(reentrantCount.incrementAndGet() == 1) { + final Thread t = Thread.currentThread(); + timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() { + @Override + public void run() { + issueDeadlock(t); + } + }, ServerConstants.LOCK_MONITOR_TIME); + } + } finally { + state.unlock(); + } + } + + private void unregisterLocking() { + state.lock(); + try { + if(reentrantCount.decrementAndGet() == 0) { + if(timeoutSchedule != null) { + timeoutSchedule.cancel(false); + timeoutSchedule = null; + } + } + + ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode); + } finally { + state.unlock(); + } + } + + private void issueDeadlock(Thread t) { + deadlockedState = t.getStackTrace(); + //super.unlock(); + } + + private static String printStackTrace(StackTraceElement[] list) { + String s = ""; + for(int i = 0; i < list.length; i++) { + s += (" " + list[i].toString() + "\r\n"); + } + + return s; + } + + @Override + public MonitoredReadLock dispose() { + state.lock(); + try { + if(timeoutSchedule != null) { + timeoutSchedule.cancel(false); + timeoutSchedule = null; + } + } finally { + state.unlock(); + } + + //unlock(); + return new EmptyReadLock(); + } +} diff --git a/src/net/server/audit/locks/active/TrackerReentrantLock.java b/src/net/server/audit/locks/active/TrackerReentrantLock.java new file mode 100644 index 0000000000..6ee53881c2 --- /dev/null +++ b/src/net/server/audit/locks/active/TrackerReentrantLock.java @@ -0,0 +1,171 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.active; + +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.ScheduledFuture; +import constants.ServerConstants; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import server.TimerManager; +import net.server.audit.ThreadTracker; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.empty.EmptyReentrantLock; +import tools.FilePrinter; + +/** + * + * @author RonanLana + */ +public class TrackerReentrantLock extends ReentrantLock implements MonitoredReentrantLock { + private ScheduledFuture timeoutSchedule = null; + private StackTraceElement[] deadlockedState = null; + private final MonitoredLockType id; + private final int hashcode; + private final Lock state = new ReentrantLock(true); + private final AtomicInteger reentrantCount = new AtomicInteger(0); + + public TrackerReentrantLock(MonitoredLockType id) { + super(); + this.id = id; + hashcode = this.hashCode(); + } + + public TrackerReentrantLock(MonitoredLockType id, boolean fair) { + super(fair); + this.id = id; + hashcode = this.hashCode(); + } + + @Override + public void lock() { + if(ServerConstants.USE_THREAD_TRACKER) { + if(deadlockedState != null) { + DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + + //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); + ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); + deadlockedState = null; + } + + registerLocking(); + } + + super.lock(); + } + + @Override + public void unlock() { + if(ServerConstants.USE_THREAD_TRACKER) { + unregisterLocking(); + } + + super.unlock(); + } + + @Override + public boolean tryLock() { + if(super.tryLock()) { + if(ServerConstants.USE_THREAD_TRACKER) { + if(deadlockedState != null) { + //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); + ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); + deadlockedState = null; + } + + registerLocking(); + } + return true; + } else { + return false; + } + } + + private void registerLocking() { + state.lock(); + try { + ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode); + + if(reentrantCount.incrementAndGet() == 1) { + final Thread t = Thread.currentThread(); + timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() { + @Override + public void run() { + issueDeadlock(t); + } + }, ServerConstants.LOCK_MONITOR_TIME); + } + } finally { + state.unlock(); + } + } + + private void unregisterLocking() { + state.lock(); + try { + if(reentrantCount.decrementAndGet() == 0) { + if(timeoutSchedule != null) { + timeoutSchedule.cancel(false); + timeoutSchedule = null; + } + } + + ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode); + } finally { + state.unlock(); + } + } + + private void issueDeadlock(Thread t) { + deadlockedState = t.getStackTrace(); + //super.unlock(); + } + + private static String printStackTrace(StackTraceElement[] list) { + String s = ""; + for(int i = 0; i < list.length; i++) { + s += (" " + list[i].toString() + "\r\n"); + } + + return s; + } + + @Override + public MonitoredReentrantLock dispose() { + state.lock(); + try { + if(timeoutSchedule != null) { + timeoutSchedule.cancel(false); + timeoutSchedule = null; + } + } finally { + state.unlock(); + } + + //unlock(); + return new EmptyReentrantLock(); + } +} diff --git a/src/net/server/audit/locks/active/TrackerWriteLock.java b/src/net/server/audit/locks/active/TrackerWriteLock.java new file mode 100644 index 0000000000..8262a6d3ff --- /dev/null +++ b/src/net/server/audit/locks/active/TrackerWriteLock.java @@ -0,0 +1,167 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft (L) 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.active; + +import constants.ServerConstants; +import java.text.DateFormat; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; +import java.util.concurrent.ScheduledFuture; +import server.TimerManager; +import net.server.audit.ThreadTracker; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.MonitoredWriteLock; +import net.server.audit.locks.empty.EmptyWriteLock; +import tools.FilePrinter; + +/** + * + * @author RonanLana + */ +public class TrackerWriteLock extends ReentrantReadWriteLock.WriteLock implements MonitoredWriteLock { + private ScheduledFuture timeoutSchedule = null; + private StackTraceElement[] deadlockedState = null; + private final MonitoredLockType id; + private final int hashcode; + private final Lock state = new ReentrantLock(true); + private final AtomicInteger reentrantCount = new AtomicInteger(0); + + public TrackerWriteLock(MonitoredReentrantReadWriteLock lock) { + super(lock); + this.id = lock.id; + hashcode = this.hashCode(); + } + + @Override + public void lock() { + if(ServerConstants.USE_THREAD_TRACKER) { + if(deadlockedState != null) { + DateFormat dateFormat = new SimpleDateFormat("dd-MM-yyyy HH:mm:ss"); + dateFormat.setTimeZone(TimeZone.getTimeZone(ServerConstants.TIMEZONE)); + + //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "[CRITICAL] " + dateFormat.format(new Date()) + " Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); + ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); + deadlockedState = null; + } + + registerLocking(); + } + + super.lock(); + } + + @Override + public void unlock() { + if(ServerConstants.USE_THREAD_TRACKER) { + unregisterLocking(); + } + + super.unlock(); + } + + @Override + public boolean tryLock() { + if(super.tryLock()) { + if(ServerConstants.USE_THREAD_TRACKER) { + if(deadlockedState != null) { + //FilePrinter.printError(FilePrinter.DEADLOCK_ERROR, "Deadlock occurred when trying to use the '" + id.name() + "' lock resources:\r\n" + printStackTrace(deadlockedState) + "\r\n\r\n"); + ThreadTracker.getInstance().accessThreadTracker(true, true, id, hashcode); + deadlockedState = null; + } + + registerLocking(); + } + return true; + } else { + return false; + } + } + + private void registerLocking() { + state.lock(); + try { + ThreadTracker.getInstance().accessThreadTracker(false, true, id, hashcode); + + if(reentrantCount.incrementAndGet() == 1) { + final Thread t = Thread.currentThread(); + timeoutSchedule = TimerManager.getInstance().schedule(new Runnable() { + @Override + public void run() { + issueDeadlock(t); + } + }, ServerConstants.LOCK_MONITOR_TIME); + } + } finally { + state.unlock(); + } + } + + private void unregisterLocking() { + state.lock(); + try { + if(reentrantCount.decrementAndGet() == 0) { + if(timeoutSchedule != null) { + timeoutSchedule.cancel(false); + timeoutSchedule = null; + } + } + + ThreadTracker.getInstance().accessThreadTracker(false, false, id, hashcode); + } finally { + state.unlock(); + } + } + + private void issueDeadlock(Thread t) { + deadlockedState = t.getStackTrace(); + //super.unlock(); + } + + private static String printStackTrace(StackTraceElement[] list) { + String s = ""; + for(int i = 0; i < list.length; i++) { + s += (" " + list[i].toString() + "\r\n"); + } + + return s; + } + + @Override + public MonitoredWriteLock dispose() { + state.lock(); + try { + if(timeoutSchedule != null) { + timeoutSchedule.cancel(false); + timeoutSchedule = null; + } + } finally { + state.unlock(); + } + + //unlock(); + return new EmptyWriteLock(); + } +} diff --git a/src/net/server/audit/locks/empty/EmptyReadLock.java b/src/net/server/audit/locks/empty/EmptyReadLock.java new file mode 100644 index 0000000000..a94dfadb26 --- /dev/null +++ b/src/net/server/audit/locks/empty/EmptyReadLock.java @@ -0,0 +1,44 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.empty; + +import net.server.audit.locks.MonitoredReadLock; + +/** + * + * @author RonanLana + */ +public class EmptyReadLock implements MonitoredReadLock { + @Override + public void lock() {} + + @Override + public void unlock() {} + + @Override + public boolean tryLock() { + return false; + } + + @Override + public MonitoredReadLock dispose() { + return null; + } +} diff --git a/src/net/server/audit/locks/empty/EmptyReentrantLock.java b/src/net/server/audit/locks/empty/EmptyReentrantLock.java new file mode 100644 index 0000000000..1ccd108795 --- /dev/null +++ b/src/net/server/audit/locks/empty/EmptyReentrantLock.java @@ -0,0 +1,44 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.empty; + +import net.server.audit.locks.MonitoredReentrantLock; + +/** + * + * @author RonanLana + */ +public class EmptyReentrantLock implements MonitoredReentrantLock { + @Override + public void lock() {} + + @Override + public void unlock() {} + + @Override + public boolean tryLock() { + return false; + } + + @Override + public MonitoredReentrantLock dispose() { + return null; + } +} diff --git a/src/net/server/audit/locks/empty/EmptyWriteLock.java b/src/net/server/audit/locks/empty/EmptyWriteLock.java new file mode 100644 index 0000000000..cff88f441b --- /dev/null +++ b/src/net/server/audit/locks/empty/EmptyWriteLock.java @@ -0,0 +1,44 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.empty; + +import net.server.audit.locks.MonitoredWriteLock; + +/** + * + * @author RonanLana + */ +public class EmptyWriteLock implements MonitoredWriteLock { + @Override + public void lock() {} + + @Override + public void unlock() {} + + @Override + public boolean tryLock() { + return false; + } + + @Override + public MonitoredWriteLock dispose() { + return null; + } +} diff --git a/src/net/server/audit/locks/factory/MonitoredReadLockFactory.java b/src/net/server/audit/locks/factory/MonitoredReadLockFactory.java new file mode 100644 index 0000000000..728e8fa94b --- /dev/null +++ b/src/net/server/audit/locks/factory/MonitoredReadLockFactory.java @@ -0,0 +1,33 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.factory; + +import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.active.TrackerReadLock; + +/** + * + * @author RonanLana + */ +public class MonitoredReadLockFactory { + public static TrackerReadLock createLock(MonitoredReentrantReadWriteLock lock) { + return new TrackerReadLock(lock); + } +} diff --git a/src/net/server/audit/locks/factory/MonitoredReentrantLockFactory.java b/src/net/server/audit/locks/factory/MonitoredReentrantLockFactory.java new file mode 100644 index 0000000000..6275fa24ec --- /dev/null +++ b/src/net/server/audit/locks/factory/MonitoredReentrantLockFactory.java @@ -0,0 +1,37 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.factory; + +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.active.TrackerReentrantLock; + +/** + * + * @author RonanLana + */ +public class MonitoredReentrantLockFactory { + public static TrackerReentrantLock createLock(MonitoredLockType id) { + return new TrackerReentrantLock(id); + } + + public static TrackerReentrantLock createLock(MonitoredLockType id, boolean fair) { + return new TrackerReentrantLock(id, fair); + } +} diff --git a/src/net/server/audit/locks/factory/MonitoredWriteLockFactory.java b/src/net/server/audit/locks/factory/MonitoredWriteLockFactory.java new file mode 100644 index 0000000000..682983082d --- /dev/null +++ b/src/net/server/audit/locks/factory/MonitoredWriteLockFactory.java @@ -0,0 +1,33 @@ +/* + This file is part of the HeavenMS MapleStory Server + Copyleft 2016 - 2018 RonanLana + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as + published by the Free Software Foundation version 3 as published by + the Free Software Foundation. You may not use, modify or distribute + this program under any other version of the GNU Affero General Public + License. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . +*/ +package net.server.audit.locks.factory; + +import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.active.TrackerWriteLock; + +/** + * + * @author RonanLana + */ +public class MonitoredWriteLockFactory { + public static TrackerWriteLock createLock(MonitoredReentrantReadWriteLock lock) { + return new TrackerWriteLock(lock); + } +} diff --git a/src/net/server/channel/Channel.java b/src/net/server/channel/Channel.java index a425bbf4f3..7a51af8685 100644 --- a/src/net/server/channel/Channel.java +++ b/src/net/server/channel/Channel.java @@ -29,6 +29,7 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Collections; +import java.util.HashSet; import java.util.Map; import java.util.Map.Entry; import java.util.Set; @@ -39,6 +40,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.MapleServerHandler; import net.mina.MapleCodecFactory; @@ -95,6 +97,7 @@ public final class Channel { private OverallScheduler channelSchedulers[] = new OverallScheduler[4]; private Map hiredMerchants = new HashMap<>(); private final Map storedVars = new HashMap<>(); + private Set playersAway = new HashSet<>(); private List expeditions = new ArrayList<>(); private List expedType = new ArrayList<>(); private MapleEvent event; @@ -128,7 +131,7 @@ public final class Channel { private MonitoredReentrantLock faceLock[] = new MonitoredReentrantLock[4]; - private MonitoredReentrantLock lock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL, true); + private MonitoredReentrantLock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL, true); public Channel(final int world, final int channel, long startTime) { this.world = world; @@ -165,7 +168,7 @@ public final class Channel { } for(int i = 0; i < 4; i++) { - faceLock[i] = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_FACEEXPRS, true); + faceLock[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_FACEEXPRS, true); mobStatusSchedulers[i] = new MobStatusScheduler(); mobAnimationSchedulers[i] = new MobAnimationScheduler(); @@ -208,6 +211,7 @@ public final class Channel { eventSM = null; closeChannelSchedules(); + players = null; acceptor.unbind(); @@ -263,10 +267,10 @@ public final class Channel { channelSchedulers[i] = null; } - faceLock[i].dispose(); + faceLock[i] = faceLock[i].dispose(); } - lock.dispose(); + lock = lock.dispose(); } private void closeAllMerchants() { @@ -291,7 +295,7 @@ public final class Channel { public int getWorld() { return world; } - + public void addPlayer(MapleCharacter chr) { players.addPlayer(chr); chr.announce(MaplePacketCreator.serverMessage(serverMessage)); @@ -355,6 +359,18 @@ public final class Channel { } return partym; } + + public void insertPlayerAway(int chrId) { // either they in CS or MTS + playersAway.add(chrId); + } + + public void removePlayerAway(int chrId) { + playersAway.remove(chrId); + } + + public boolean canUninstall() { + return players.getSize() == 0 && playersAway.isEmpty(); + } public class respawnMaps implements Runnable { diff --git a/src/net/server/channel/handlers/AbstractDealDamageHandler.java b/src/net/server/channel/handlers/AbstractDealDamageHandler.java index 4b36d26253..3ab45935e1 100644 --- a/src/net/server/channel/handlers/AbstractDealDamageHandler.java +++ b/src/net/server/channel/handlers/AbstractDealDamageHandler.java @@ -152,26 +152,27 @@ public abstract class AbstractDealDamageHandler extends AbstractMaplePacketHandl AutobanFactory.MPCON.addPoint(player.getAutobanManager(), "Skill: " + attack.skill + "; Player MP: " + player.getMp() + "; MP Needed: " + attackEffect.getMpCon()); } + int mobCount = attackEffect.getMobCount(); if (attack.skill != Cleric.HEAL) { if (player.isAlive()) { - if(attack.skill == NightWalker.POISON_BOMB) // Poison Bomb + if(attack.skill == NightWalker.POISON_BOMB) {// Poison Bomb attackEffect.applyTo(player, new Point(attack.position.x, attack.position.y)); - else if(attack.skill != Aran.BODY_PRESSURE) // prevent BP refreshing + } else if(attack.skill != Aran.BODY_PRESSURE) {// prevent BP refreshing attackEffect.applyTo(player); + + if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD + || attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM || attack.skill == WindArcher.FINAL_ATTACK + || attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) { + + mobCount = 15;//:( + } else if (attack.skill == Aran.HIDDEN_FULL_DOUBLE || attack.skill == Aran.HIDDEN_FULL_TRIPLE || attack.skill == Aran.HIDDEN_OVER_DOUBLE || attack.skill == Aran.HIDDEN_OVER_TRIPLE) { + mobCount = 12; + } + } } else { player.getClient().announce(MaplePacketCreator.enableActions()); } } - int mobCount = attackEffect.getMobCount(); - if (attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Page.FINAL_ATTACK_BW || attack.skill == Page.FINAL_ATTACK_SWORD || attack.skill == Fighter.FINAL_ATTACK_SWORD - || attack.skill == Fighter.FINAL_ATTACK_AXE || attack.skill == Spearman.FINAL_ATTACK_SPEAR || attack.skill == Spearman.FINAL_ATTACK_POLEARM || attack.skill == WindArcher.FINAL_ATTACK - || attack.skill == DawnWarrior.FINAL_ATTACK || attack.skill == Hunter.FINAL_ATTACK || attack.skill == Crossbowman.FINAL_ATTACK) { - mobCount = 15;//:( - } - - if (attack.skill == Aran.HIDDEN_FULL_DOUBLE || attack.skill == Aran.HIDDEN_FULL_TRIPLE || attack.skill == Aran.HIDDEN_OVER_DOUBLE || attack.skill == Aran.HIDDEN_OVER_TRIPLE) { - mobCount = 12; - } if (attack.numAttacked > mobCount) { AutobanFactory.MOB_COUNT.autoban(player, "Skill: " + attack.skill + "; Count: " + attack.numAttacked + " Max: " + attackEffect.getMobCount()); diff --git a/src/net/server/channel/handlers/AutoAssignHandler.java b/src/net/server/channel/handlers/AutoAssignHandler.java index bb1960543a..b9e2717c4f 100644 --- a/src/net/server/channel/handlers/AutoAssignHandler.java +++ b/src/net/server/channel/handlers/AutoAssignHandler.java @@ -21,22 +21,9 @@ */ package net.server.channel.handlers; -import constants.ServerConstants; -import client.MapleCharacter; import client.MapleClient; -import client.MapleJob; -import client.MapleStat; -import client.autoban.AutobanFactory; -import client.inventory.Equip; -import client.inventory.Item; -import client.inventory.MapleInventory; -import client.inventory.MapleInventoryType; -import java.util.Collection; -import java.util.Collections; -import java.util.List; -import java.util.ArrayList; +import client.processor.AssignAPProcessor; import net.AbstractMaplePacketHandler; -import tools.MaplePacketCreator; import tools.data.input.SeekableLittleEndianAccessor; /** @@ -45,382 +32,8 @@ import tools.data.input.SeekableLittleEndianAccessor; */ public class AutoAssignHandler extends AbstractMaplePacketHandler { - private static int getNthHighestStat(List statList, short rank) { // ranks from 0 - return(statList.size() <= rank ? 0 : statList.get(rank)); - } - @Override public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { - MapleCharacter chr = c.getPlayer(); - if (chr.getRemainingAp() < 1) return; - - int[] statGain = new int[4]; - int[] statEqpd = new int[4]; - statGain[0] = 0; statGain[1] = 0; statGain[2] = 0; statGain[3] = 0; - - slea.skip(8); - - if(ServerConstants.USE_SERVER_AUTOASSIGNER) { - // --------- Ronan Lana's AUTOASSIGNER --------- - // This method excels for assigning APs in such a way to cover all equipments AP requirements. - byte opt = slea.readByte(); // useful for pirate autoassigning - - int str = 0, dex = 0, luk = 0, int_ = 0; - List eqpStrList = new ArrayList<>(); - List eqpDexList = new ArrayList<>(); - List eqpLukList = new ArrayList<>(); - - MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED); - Collection equippedC = iv.list(); - Equip nEquip; - - for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item. - nEquip = (Equip)item; - if(nEquip.getStr() > 0) eqpStrList.add(nEquip.getStr()); - str += nEquip.getStr(); - - if(nEquip.getDex() > 0) eqpDexList.add(nEquip.getDex()); - dex += nEquip.getDex(); - - if(nEquip.getLuk() > 0) eqpLukList.add(nEquip.getLuk()); - luk += nEquip.getLuk(); - - //if(nEquip.getInt() > 0) eqpIntList.add(nEquip.getInt()); //not needed... - int_ += nEquip.getInt(); - } - - statEqpd[0] = str; - statEqpd[1] = dex; - statEqpd[2] = luk; - statEqpd[3] = int_; - - Collections.sort(eqpStrList, Collections.reverseOrder()); - Collections.sort(eqpDexList, Collections.reverseOrder()); - Collections.sort(eqpLukList, Collections.reverseOrder()); - - //Autoassigner looks up the 1st/2nd placed equips for their stats to calculate the optimal upgrade. - int eqpStr = getNthHighestStat(eqpStrList, (short) 0) + getNthHighestStat(eqpStrList, (short) 1); - int eqpDex = getNthHighestStat(eqpDexList, (short) 0) + getNthHighestStat(eqpDexList, (short) 1); - int eqpLuk = getNthHighestStat(eqpLukList, (short) 0) + getNthHighestStat(eqpLukList, (short) 1); - - //c.getPlayer().message("----------------------------------------"); - //c.getPlayer().message("SDL: s" + eqpStr + " d" + eqpDex + " l" + eqpLuk + " BASE STATS --> STR: " + chr.getStr() + " DEX: " + chr.getDex() + " INT: " + chr.getInt() + " LUK: " + chr.getLuk()); - //c.getPlayer().message("SUM EQUIP STATS -> STR: " + str + " DEX: " + dex + " LUK: " + luk + " INT: " + int_); - - MapleJob stance = c.getPlayer().getJobStyle(opt); - int prStat = 0, scStat = 0, trStat = 0, temp, tempAp = chr.getRemainingAp(), CAP; - - MapleStat primary, secondary, tertiary = MapleStat.LUK; - switch(stance) { - case MAGICIAN: - CAP = 165; - scStat = (chr.getLevel() + 3) - (chr.getLuk() + luk - eqpLuk); - if(scStat < 0) scStat = 0; - scStat = Math.min(scStat, tempAp); - - if(tempAp > scStat) tempAp -= scStat; - else tempAp = 0; - - prStat = tempAp; - int_ = prStat; - luk = scStat; - str = 0; dex = 0; - - if(luk + chr.getLuk() > CAP) { - temp = luk + chr.getLuk() - CAP; - luk -= temp; - int_ += temp; - } - - primary = MapleStat.INT; - secondary = MapleStat.LUK; - tertiary = MapleStat.DEX; - - break; - - case BOWMAN: - CAP = 125; - scStat = (chr.getLevel() + 5) - (chr.getStr() + str - eqpStr); - if(scStat < 0) scStat = 0; - scStat = Math.min(scStat, tempAp); - - if(tempAp > scStat) tempAp -= scStat; - else tempAp = 0; - - prStat = tempAp; - dex = prStat; - str = scStat; - int_ = 0; luk = 0; - - if(str + chr.getStr() > CAP) { - temp = str + chr.getStr() - CAP; - str -= temp; - dex += temp; - } - - primary = MapleStat.DEX; - secondary = MapleStat.STR; - - break; - - case GUNSLINGER: - case CROSSBOWMAN: - CAP = 120; - scStat = chr.getLevel() - (chr.getStr() + str - eqpStr); - if(scStat < 0) scStat = 0; - scStat = Math.min(scStat, tempAp); - - if(tempAp > scStat) tempAp -= scStat; - else tempAp = 0; - - prStat = tempAp; - dex = prStat; - str = scStat; - int_ = 0; luk = 0; - - if(str + chr.getStr() > CAP) { - temp = str + chr.getStr() - CAP; - str -= temp; - dex += temp; - } - - primary = MapleStat.DEX; - secondary = MapleStat.STR; - - break; - - case THIEF: - CAP = 160; - - scStat = 0; - if(chr.getDex() < 80) { - scStat = (2 * chr.getLevel()) - (chr.getDex() + dex - eqpDex); - if(scStat < 0) scStat = 0; - - scStat = Math.min(80 - chr.getDex(), scStat); - scStat = Math.min(tempAp, scStat); - tempAp -= scStat; - } - - temp = (chr.getLevel() + 40) - Math.max(80, scStat + chr.getDex() + dex - eqpDex); - if(temp < 0) temp = 0; - temp = Math.min(tempAp, temp); - scStat += temp; - tempAp -= temp; - - // thieves will upgrade STR as well only if a level-based threshold is reached. - if(chr.getStr() >= Math.max(13, (int)(0.4 * chr.getLevel()))) { - if(chr.getStr() < 50) { - trStat = (chr.getLevel() - 10) - (chr.getStr() + str - eqpStr); - if(trStat < 0) trStat = 0; - - trStat = Math.min(50 - chr.getStr(), trStat); - trStat = Math.min(tempAp, trStat); - tempAp -= trStat; - } - - temp = (20 + (chr.getLevel() / 2)) - Math.max(50, trStat + chr.getStr() + str - eqpStr); - if(temp < 0) temp = 0; - temp = Math.min(tempAp, temp); - trStat += temp; - tempAp -= temp; - } - - prStat = tempAp; - luk = prStat; - dex = scStat; - str = trStat; - int_ = 0; - - if(dex + chr.getDex() > CAP) { - temp = dex + chr.getDex() - CAP; - dex -= temp; - luk += temp; - } - if(str + chr.getStr() > CAP) { - temp = str + chr.getStr() - CAP; - str -= temp; - luk += temp; - } - - primary = MapleStat.LUK; - secondary = MapleStat.DEX; - tertiary = MapleStat.STR; - - break; - - case BRAWLER: - CAP = 120; - - scStat = chr.getLevel() - (chr.getDex() + dex - eqpDex); - if(scStat < 0) scStat = 0; - scStat = Math.min(scStat, tempAp); - - if(tempAp > scStat) tempAp -= scStat; - else tempAp = 0; - - prStat = tempAp; - str = prStat; - dex = scStat; - int_ = 0; luk = 0; - - if(dex + chr.getDex() > CAP) { - temp = dex + chr.getDex() - CAP; - dex -= temp; - str += temp; - } - - primary = MapleStat.STR; - secondary = MapleStat.DEX; - - break; - - default: //warrior, beginner, ... - CAP = 80; - - scStat = ((2 * chr.getLevel()) / 3) - (chr.getDex() + dex - eqpDex); - if(scStat < 0) scStat = 0; - scStat = Math.min(scStat, tempAp); - - if(tempAp > scStat) tempAp -= scStat; - else tempAp = 0; - - prStat = tempAp; - str = prStat; - dex = scStat; - int_ = 0; luk = 0; - - if(dex + chr.getDex() > CAP) { - temp = dex + chr.getDex() - CAP; - dex -= temp; - str += temp; - } - - primary = MapleStat.STR; - secondary = MapleStat.DEX; - } - - //------------------------------------------------------------------------------------- - - int extras = 0; - - extras = gainStatByType(chr, primary, statGain, prStat + extras); - extras = gainStatByType(chr, secondary, statGain, scStat + extras); - extras = gainStatByType(chr, tertiary, statGain, trStat + extras); - - if(extras > 0) { //redistribute surplus in priority order - extras = gainStatByType(chr, primary, statGain, extras); - extras = gainStatByType(chr, secondary, statGain, extras); - extras = gainStatByType(chr, tertiary, statGain, extras); - gainStatByType(chr, getQuaternaryStat(stance), statGain, extras); - } - - int remainingAp = (chr.getRemainingAp() - getAccumulatedStatGain(statGain)); - chr.setRemainingAp(remainingAp); - chr.updateSingleStat(MapleStat.AVAILABLEAP, remainingAp); - c.announce(MaplePacketCreator.enableActions()); - - //---------------------------------------------------------------------------------------- - - c.announce(MaplePacketCreator.serverNotice(1, "Better AP applications detected:\r\nSTR: +" + statGain[0] + "\r\nDEX: +" + statGain[1] + "\r\nINT: +" + statGain[3] + "\r\nLUK: +" + statGain[2])); - } else { - if(slea.available() < 16) { - AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign."); - c.disconnect(false, false); - return; - } - - MapleInventory iv = chr.getInventory(MapleInventoryType.EQUIPPED); - Collection equippedC = iv.list(); - for (Item item : equippedC) { //selecting the biggest AP value of each stat from each equipped item. - Equip nEquip = (Equip)item; - - statEqpd[0] += nEquip.getStr(); - statEqpd[1] += nEquip.getDex(); - statEqpd[2] += nEquip.getLuk(); - statEqpd[3] += nEquip.getInt(); - } - - int total = 0; - int extras = 0; - for (int i = 0; i < 2; i++) { - int type = slea.readInt(); - int tempVal = slea.readInt(); - if (tempVal < 0 || tempVal > c.getPlayer().getRemainingAp()) { - return; - } - total += tempVal; - extras += gainStatByType(chr, MapleStat.getBy5ByteEncoding(type), statGain, tempVal); - } - int remainingAp = (chr.getRemainingAp() - total) + extras; - chr.setRemainingAp(remainingAp); - chr.updateSingleStat(MapleStat.AVAILABLEAP, remainingAp); - c.announce(MaplePacketCreator.enableActions()); - } - } - - private int gainStatByType(MapleCharacter chr, MapleStat type, int[] statGain, int gain) { - if(gain <= 0) return 0; - - int newVal = 0; - if (type.equals(MapleStat.STR)) { - newVal = chr.getStr() + gain; - if (newVal > ServerConstants.MAX_AP) { - statGain[0] += (gain - (newVal - ServerConstants.MAX_AP)); - chr.setStr(ServerConstants.MAX_AP); - } else { - statGain[0] += gain; - chr.setStr(newVal); - } - } else if (type.equals(MapleStat.INT)) { - newVal = chr.getInt() + gain; - if (newVal > ServerConstants.MAX_AP) { - statGain[3] += (gain - (newVal - ServerConstants.MAX_AP)); - chr.setInt(ServerConstants.MAX_AP); - } else { - statGain[3] += gain; - chr.setInt(newVal); - } - } else if (type.equals(MapleStat.LUK)) { - newVal = chr.getLuk() + gain; - if (newVal > ServerConstants.MAX_AP) { - statGain[2] += (gain - (newVal - ServerConstants.MAX_AP)); - chr.setLuk(ServerConstants.MAX_AP); - } else { - statGain[2] += gain; - chr.setLuk(newVal); - } - } else if (type.equals(MapleStat.DEX)) { - newVal = chr.getDex() + gain; - if (newVal > ServerConstants.MAX_AP) { - statGain[1] += (gain - (newVal - ServerConstants.MAX_AP)); - chr.setDex(ServerConstants.MAX_AP); - } else { - statGain[1] += gain; - chr.setDex(newVal); - } - } - - if (newVal > ServerConstants.MAX_AP) { - chr.updateSingleStat(type, ServerConstants.MAX_AP); - return newVal - ServerConstants.MAX_AP; - } - chr.updateSingleStat(type, newVal); - return 0; - } - - private MapleStat getQuaternaryStat(MapleJob stance) { - if(stance != MapleJob.MAGICIAN) return MapleStat.INT; - return MapleStat.STR; - } - - private int getAccumulatedStatGain(int[] statGain) { - int acc = 0; - - for(byte i = 0; i < statGain.length; i++) { - acc += statGain[i]; - } - - return acc; + AssignAPProcessor.APAutoAssignAction(slea, c); } } diff --git a/src/net/server/channel/handlers/ChangeChannelHandler.java b/src/net/server/channel/handlers/ChangeChannelHandler.java index feece8abda..0f343fe5d1 100644 --- a/src/net/server/channel/handlers/ChangeChannelHandler.java +++ b/src/net/server/channel/handlers/ChangeChannelHandler.java @@ -37,12 +37,13 @@ public final class ChangeChannelHandler extends AbstractMaplePacketHandler { int channel = slea.readByte() + 1; c.getPlayer().getAutobanManager().setTimestamp(6, slea.readInt(), 2); if(c.getChannel() == channel) { - AutobanFactory.GENERAL.alert(c.getPlayer(), "CCing to same channel."); - c.disconnect(false, false); - return; + AutobanFactory.GENERAL.alert(c.getPlayer(), "CCing to same channel."); + c.disconnect(false, false); + return; } else if (c.getPlayer().getCashShop().isOpened() || c.getPlayer().getMiniGame() != null || c.getPlayer().getPlayerShop() != null) { return; } + c.changeChannel(channel); } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/DistributeAPHandler.java b/src/net/server/channel/handlers/DistributeAPHandler.java index 5d9ac714fd..3831a15e5d 100644 --- a/src/net/server/channel/handlers/DistributeAPHandler.java +++ b/src/net/server/channel/handlers/DistributeAPHandler.java @@ -21,319 +21,18 @@ */ package net.server.channel.handlers; -import client.MapleCharacter; import client.MapleClient; -import client.MapleJob; -import client.MapleStat; -import client.Skill; -import client.SkillFactory; -import constants.ServerConstants; -import constants.skills.BlazeWizard; -import constants.skills.Brawler; -import constants.skills.DawnWarrior; -import constants.skills.Magician; -import constants.skills.Warrior; +import client.processor.AssignAPProcessor; import net.AbstractMaplePacketHandler; -import tools.MaplePacketCreator; -import tools.Randomizer; import tools.data.input.SeekableLittleEndianAccessor; public final class DistributeAPHandler extends AbstractMaplePacketHandler { - private static final int max = 32767; - + @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { slea.readInt(); int num = slea.readInt(); - if (c.getPlayer().getRemainingAp() > 0) { - if (addStat(c, num, false)) { - c.getPlayer().setRemainingAp(c.getPlayer().getRemainingAp() - 1); - c.getPlayer().updateSingleStat(MapleStat.AVAILABLEAP, c.getPlayer().getRemainingAp()); - } - } - c.announce(MaplePacketCreator.enableActions()); - } - - public static boolean addStat(MapleClient c, int apTo, boolean usedAPReset) { - switch (apTo) { - case 64: // Str - if (c.getPlayer().getStr() >= max) { - return false; - } - c.getPlayer().addStat(1, 1); - break; - case 128: // Dex - if (c.getPlayer().getDex() >= max) { - return false; - } - c.getPlayer().addStat(2, 1); - break; - case 256: // Int - if (c.getPlayer().getInt() >= max) { - return false; - } - c.getPlayer().addStat(3, 1); - break; - case 512: // Luk - if (c.getPlayer().getLuk() >= max) { - return false; - } - c.getPlayer().addStat(4, 1); - break; - case 2048: // HP - addHP(c.getPlayer(), addHP(c, usedAPReset)); - break; - case 8192: // MP - addMP(c.getPlayer(), addMP(c, usedAPReset)); - break; - default: - c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, c.getPlayer())); - return false; - } - return true; - } - - private static int addHP(MapleClient c, boolean usedAPReset) { - MapleCharacter player = c.getPlayer(); - MapleJob job = player.getJob(); - int MaxHP = player.getMaxHp(); - if (player.getHpMpApUsed() > 9999 || MaxHP >= 30000) { - return MaxHP; - } - return MaxHP + calcHpChange(player, job, usedAPReset); - } - - private static int calcHpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) { - int MaxHP = 0; - - if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) { - if(!usedAPReset) { - Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.DAWNWARRIOR1) ? DawnWarrior.MAX_HP_INCREASE : Warrior.IMPROVED_MAXHP); - int sLvl = player.getSkillLevel(increaseHP); - - if(sLvl > 0) - MaxHP += increaseHP.getEffect(sLvl).getY(); - } - - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if (usedAPReset) { - MaxHP += 20; - } else { - MaxHP += Randomizer.rand(18, 22); - } - } else { - MaxHP += 20; - } - } else if(job.isA(MapleJob.ARAN1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if (usedAPReset) { - MaxHP += 20; - } else { - MaxHP += Randomizer.rand(26, 30); - } - } else { - MaxHP += 28; - } - } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if (usedAPReset) { - MaxHP += 6; - } else { - MaxHP += Randomizer.rand(5, 9); - } - } else { - MaxHP += 6; - } - } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if (usedAPReset) { - MaxHP += 16; - } else { - MaxHP += Randomizer.rand(14, 18); - } - } else { - MaxHP += 16; - } - } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if (usedAPReset) { - MaxHP += 16; - } else { - MaxHP += Randomizer.rand(14, 18); - } - } else { - MaxHP += 16; - } - } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { - if(!usedAPReset) { - Skill increaseHP = SkillFactory.getSkill(Brawler.IMPROVE_MAX_HP); - int sLvl = player.getSkillLevel(increaseHP); - - if(sLvl > 0) - MaxHP += increaseHP.getEffect(sLvl).getY(); - } - - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if (usedAPReset) { - MaxHP += 18; - } else { - MaxHP += Randomizer.rand(16, 20); - } - } else { - MaxHP += 18; - } - } else if (usedAPReset) { - MaxHP += 8; - } else { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - MaxHP += Randomizer.rand(8, 12); - } else { - MaxHP += 10; - } - } - - return MaxHP; - } - - private static int addMP(MapleClient c, boolean usedAPReset) { - MapleCharacter player = c.getPlayer(); - int MaxMP = player.getMaxMp(); - MapleJob job = player.getJob(); - if (player.getHpMpApUsed() > 9999 || player.getMaxMp() >= 30000) { - return MaxMP; - } - - return MaxMP + calcMpChange(player, job, usedAPReset); - } - - private static int calcMpChange(MapleCharacter player, MapleJob job, boolean usedAPReset) { - int MaxMP = 0; - - if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if(!usedAPReset) { - MaxMP += (Randomizer.rand(2, 4) + (player.getInt() / 10)); - } else { - MaxMP += 2; - } - } else { - MaxMP += 3; - } - } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { - if(!usedAPReset) { - Skill increaseMP = SkillFactory.getSkill(job.isA(MapleJob.BLAZEWIZARD1) ? BlazeWizard.INCREASING_MAX_MP : Magician.IMPROVED_MAX_MP_INCREASE); - int sLvl = player.getSkillLevel(increaseMP); - - if(sLvl > 0) - MaxMP += increaseMP.getEffect(sLvl).getY(); - } - - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if(!usedAPReset) { - MaxMP += (Randomizer.rand(12, 16) + (player.getInt() / 20)); - } else { - MaxMP += 18; - } - } else { - MaxMP += 18; - } - } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if(!usedAPReset) { - MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10)); - } else { - MaxMP += 10; - } - } else { - MaxMP += 10; - } - } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if(!usedAPReset) { - MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10)); - } else { - MaxMP += 10; - } - } else { - MaxMP += 10; - } - } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if(!usedAPReset) { - MaxMP += (Randomizer.rand(7, 9) + (player.getInt() / 10)); - } else { - MaxMP += 14; - } - } else { - MaxMP += 14; - } - } else { - if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) { - if(!usedAPReset) { - MaxMP += (Randomizer.rand(4, 6) + (player.getInt() / 10)); - } else { - MaxMP += 6; - } - } else { - MaxMP += 6; - } - } - - return MaxMP; - } - - private static void addHP(MapleCharacter player, int MaxHP) { - MaxHP = Math.min(30000, MaxHP); - player.setHpMpApUsed(player.getHpMpApUsed() + 1); - player.setMaxHp(MaxHP); - player.updateSingleStat(MapleStat.MAXHP, MaxHP); - } - - private static void addMP(MapleCharacter player, int MaxMP) { - MaxMP = Math.min(30000, MaxMP); - player.setHpMpApUsed(player.getHpMpApUsed() + 1); - player.setMaxMp(MaxMP); - player.updateSingleStat(MapleStat.MAXMP, MaxMP); - } - - public static int takeHp(MapleCharacter player, MapleJob job) { - int MaxHP = 0; - - if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) { - MaxHP += 54; - } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { - MaxHP += 10; - } else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { - MaxHP += 20; - } else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { - MaxHP += 20; - } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { - MaxHP += 42; - } else { - MaxHP += 12; - } - - return MaxHP; - } - - public static int takeMp(MapleCharacter player, MapleJob job) { - int MaxMP = 0; - - if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) { - MaxMP += 4; - } else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) { - MaxMP += 31; - } else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) { - MaxMP += 12; - } else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) { - MaxMP += 12; - } else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) { - MaxMP += 16; - } else { - MaxMP += 8; - } - - return MaxMP; - } + AssignAPProcessor.APAssignAction(c, num); + } } diff --git a/src/net/server/channel/handlers/DistributeSPHandler.java b/src/net/server/channel/handlers/DistributeSPHandler.java index c4bc963cb7..8e1e570c03 100644 --- a/src/net/server/channel/handlers/DistributeSPHandler.java +++ b/src/net/server/channel/handlers/DistributeSPHandler.java @@ -21,64 +21,17 @@ */ package net.server.channel.handlers; -import net.AbstractMaplePacketHandler; -import tools.FilePrinter; -import tools.MaplePacketCreator; -import tools.data.input.SeekableLittleEndianAccessor; -import client.MapleCharacter; import client.MapleClient; -import client.MapleStat; -import client.Skill; -import client.SkillFactory; -import client.autoban.AutobanFactory; -import constants.GameConstants; -import constants.skills.Aran; +import client.processor.AssignSPProcessor; +import net.AbstractMaplePacketHandler; +import tools.data.input.SeekableLittleEndianAccessor; public final class DistributeSPHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { slea.readInt(); int skillid = slea.readInt(); - if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) { - c.getSession().write(MaplePacketCreator.enableActions()); - return; - } - MapleCharacter player = c.getPlayer(); - int remainingSp = player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)); - boolean isBeginnerSkill = false; - if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) { - AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp."); - FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.\r\n"); - c.disconnect(true, false); - return; - } - if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) { - int total = 0; - for (int i = 0; i < 3; i++) { - total += player.getSkillLevel(SkillFactory.getSkill(player.getJobType() * 10000000 + 1000 + i)); - } - remainingSp = Math.min((player.getLevel() - 1), 6) - total; - isBeginnerSkill = true; - } - Skill skill = SkillFactory.getSkill(skillid); - int curLevel = player.getSkillLevel(skill); - if ((remainingSp > 0 && curLevel + 1 <= (skill.isFourthJob() ? player.getMasterLevel(skill) : skill.getMaxLevel()))) { - if (!isBeginnerSkill) { - player.setRemainingSp(player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000)) - 1, GameConstants.getSkillBook(skillid/10000)); - } - player.updateSingleStat(MapleStat.AVAILABLESP, player.getRemainingSpBySkill(GameConstants.getSkillBook(skillid/10000))); - if (skill.getId() == Aran.FULL_SWING) { - player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill)); - player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill)); - player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_FULL_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill)); - } else if (skill.getId() == Aran.OVER_SWING) { - player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill)); - player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_DOUBLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill)); - player.changeSkillLevel(SkillFactory.getSkill(Aran.HIDDEN_OVER_TRIPLE), (byte) player.getSkillLevel(skill), player.getMasterLevel(skill), player.getSkillExpiration(skill)); - } else { - player.changeSkillLevel(skill, (byte) (curLevel + 1), player.getMasterLevel(skill), player.getSkillExpiration(skill)); - } - } + AssignSPProcessor.SPAssignAction(c, skillid); } } \ No newline at end of file diff --git a/src/net/server/channel/handlers/EnterCashShopHandler.java b/src/net/server/channel/handlers/EnterCashShopHandler.java index 3becadcb9b..4d6c044de8 100644 --- a/src/net/server/channel/handlers/EnterCashShopHandler.java +++ b/src/net/server/channel/handlers/EnterCashShopHandler.java @@ -60,7 +60,7 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler { mc.unregisterChairBuff(); Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(mc.getId(), mc.getAllBuffs()); Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(mc.getId(), mc.getAllDiseases()); - mc.setAwayFromWorld(true); + mc.setAwayFromChannelWorld(); mc.notifyMapTransferToPartner(-1); mc.cancelAllBuffs(true); mc.cancelAllDebuffs(); diff --git a/src/net/server/channel/handlers/EnterMTSHandler.java b/src/net/server/channel/handlers/EnterMTSHandler.java index d6bf850f16..c2833ba12d 100644 --- a/src/net/server/channel/handlers/EnterMTSHandler.java +++ b/src/net/server/channel/handlers/EnterMTSHandler.java @@ -83,7 +83,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { chr.unregisterChairBuff(); Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs()); Server.getInstance().getPlayerBuffStorage().addDiseasesToStorage(chr.getId(), chr.getAllDiseases()); - chr.setAwayFromWorld(true); + chr.setAwayFromChannelWorld(); chr.notifyMapTransferToPartner(-1); chr.cancelAllBuffs(true); chr.cancelAllDebuffs(); @@ -96,6 +96,8 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler { chr.cancelQuestExpirationTask(); chr.saveCharToDB(); + + c.getChannelServer().removePlayer(chr); chr.getMap().removePlayer(c.getPlayer()); try { c.announce(MaplePacketCreator.openCashShop(c, true)); diff --git a/src/net/server/channel/handlers/HealOvertimeHandler.java b/src/net/server/channel/handlers/HealOvertimeHandler.java index 04b426c268..72d8ee7abf 100644 --- a/src/net/server/channel/handlers/HealOvertimeHandler.java +++ b/src/net/server/channel/handlers/HealOvertimeHandler.java @@ -26,6 +26,7 @@ import client.MapleClient; import client.autoban.AutobanFactory; import client.autoban.AutobanManager; import net.AbstractMaplePacketHandler; +import server.maps.MapleMapFactory; import tools.data.input.SeekableLittleEndianAccessor; import tools.MaplePacketCreator; @@ -33,6 +34,8 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler { @Override public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) { MapleCharacter chr = c.getPlayer(); + if(!chr.isLoggedinWorld()) return; + AutobanManager abm = chr.getAutobanManager(); int timestamp = slea.readInt(); abm.setTimestamp(0, timestamp, 3); @@ -41,8 +44,7 @@ public final class HealOvertimeHandler extends AbstractMaplePacketHandler { if (healHP != 0) { if ((abm.getLastSpam(0) + 1500) > timestamp) AutobanFactory.FAST_HP_HEALING.addPoint(abm, "Fast hp healing"); - int abHeal = 140; - if(chr.getMapId() == 105040401 || chr.getMapId() == 105040402 || chr.getMapId() == 809000101 || chr.getMapId() == 809000201) abHeal += 40; // Sleepywood sauna and showa spa... + int abHeal = 120 + (int)(20 * MapleMapFactory.getMapRecoveryRate(chr.getMapId())); // Sleepywood sauna and showa spa... if (healHP > abHeal) { AutobanFactory.HIGH_HP_HEALING.autoban(chr, "Healing: " + healHP + "; Max is " + abHeal + "."); return; diff --git a/src/net/server/channel/handlers/PlayerLoggedinHandler.java b/src/net/server/channel/handlers/PlayerLoggedinHandler.java index b89692ebcc..0560264ec5 100644 --- a/src/net/server/channel/handlers/PlayerLoggedinHandler.java +++ b/src/net/server/channel/handlers/PlayerLoggedinHandler.java @@ -42,6 +42,7 @@ import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.Pair; import tools.data.input.SeekableLittleEndianAccessor; +import client.BuddyList; import client.BuddylistEntry; import client.CharacterNameAndId; import client.MapleCharacter; @@ -101,7 +102,17 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { int state = c.getLoginState(); boolean allowLogin = true; + Channel cserv = c.getChannelServer(); + if(cserv == null) { + c.setChannel(1); + cserv = c.getChannelServer(); + + if(cserv == null) { // world server is out + c.disconnect(true, false); + return; + } + } /* is this check really necessary? if (state == MapleClient.LOGIN_SERVER_TRANSITION || state == MapleClient.LOGIN_NOTLOGGEDIN) { @@ -126,7 +137,11 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { } c.updateLoginState(MapleClient.LOGIN_LOGGEDIN); + World world = server.getWorld(c.getWorld()); + cserv.addPlayer(player); + world.addPlayer(player); + player.setEnteredChannelWorld(); List buffs = server.getPlayerBuffStorage().getBuffsFromStorage(cid); if (buffs != null) { @@ -156,19 +171,16 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler { player.getMap().addPlayer(player); player.visitMap(player.getMap()); - World world = server.getWorld(c.getWorld()); - world.getPlayerStorage().addPlayer(player); - - player.setAwayFromWorld(false); - - int buddyIds[] = player.getBuddylist().getBuddyIds(); + BuddyList bl = player.getBuddylist(); + int buddyIds[] = bl.getBuddyIds(); world.loggedOn(player.getName(), player.getId(), c.getChannel(), buddyIds); - for (CharacterIdChannelPair onlineBuddy : server.getWorld(c.getWorld()).multiBuddyFind(player.getId(), buddyIds)) { - BuddylistEntry ble = player.getBuddylist().get(onlineBuddy.getCharacterId()); + for (CharacterIdChannelPair onlineBuddy : world.multiBuddyFind(player.getId(), buddyIds)) { + BuddylistEntry ble = bl.get(onlineBuddy.getCharacterId()); ble.setChannel(onlineBuddy.getChannel()); - player.getBuddylist().put(ble); + bl.put(ble); } - c.announce(MaplePacketCreator.updateBuddylist(player.getBuddylist().getBuddies())); + c.announce(MaplePacketCreator.updateBuddylist(bl.getBuddies())); + c.announce(MaplePacketCreator.loadFamily(player)); if (player.getFamilyId() > 0) { MapleFamily f = world.getFamily(player.getFamilyId()); diff --git a/src/net/server/channel/handlers/UseCashItemHandler.java b/src/net/server/channel/handlers/UseCashItemHandler.java index 65fea04f2a..d0f4e489be 100644 --- a/src/net/server/channel/handlers/UseCashItemHandler.java +++ b/src/net/server/channel/handlers/UseCashItemHandler.java @@ -23,8 +23,6 @@ package net.server.channel.handlers; import client.MapleCharacter; import client.MapleClient; -import client.MapleJob; -import client.MapleStat; import client.Skill; import client.SkillFactory; import client.creator.veteran.*; @@ -36,6 +34,7 @@ import client.inventory.MaplePet; import client.inventory.ModifyInventory; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; +import client.processor.AssignAPProcessor; import constants.GameConstants; import constants.ItemConstants; import constants.ServerConstants; @@ -79,9 +78,9 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { slea.readShort(); int itemId = slea.readInt(); int itemType = itemId / 10000; - Item toUse = c.getPlayer().getInventory(MapleInventoryType.CASH).getItem(c.getPlayer().getInventory(MapleInventoryType.CASH).findById(itemId).getPosition()); + Item toUse = player.getInventory(MapleInventoryType.CASH).getItem(player.getInventory(MapleInventoryType.CASH).findById(itemId).getPosition()); String medal = ""; - Item medalItem = c.getPlayer().getInventory(MapleInventoryType.EQUIPPED).getItem((short) -49); + Item medalItem = player.getInventory(MapleInventoryType.EQUIPPED).getItem((short) -49); if (medalItem != null) { medal = "<" + ii.getName(medalItem.getItemId()) + "> "; } @@ -100,7 +99,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { player.changeMap(c.getChannelServer().getMapFactory().getMap(mapId)); } else { MapleInventoryManipulator.addById(c, itemId, (short) 1); - c.getPlayer().dropMessage(1, error1); + player.dropMessage(1, error1); c.announce(MaplePacketCreator.enableActions()); } } else { @@ -149,131 +148,12 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { player.changeSkillLevel(skillSPTo, (byte) (curLevel + 1), player.getMasterLevel(skillSPTo), -1); } } else { - List> statupdate = new ArrayList<>(2); int APTo = slea.readInt(); int APFrom = slea.readInt(); - switch (APFrom) { - case 64: // str - if (player.getStr() < 5) { - c.getPlayer().message("You don't have the minimum STR required to swap."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - player.addStat(1, -1); - break; - case 128: // dex - if (player.getDex() < 5) { - c.getPlayer().message("You don't have the minimum DEX required to swap."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - player.addStat(2, -1); - break; - case 256: // int - if (player.getInt() < 5) { - c.getPlayer().message("You don't have the minimum INT required to swap."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - player.addStat(3, -1); - break; - case 512: // luk - if (player.getLuk() < 5) { - c.getPlayer().message("You don't have the minimum LUK required to swap."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - player.addStat(4, -1); - break; - case 2048: // HP - if(ServerConstants.USE_ENFORCE_HPMP_SWAP) { - if (APTo != 8192) { - c.getPlayer().message("You can only swap HP ability points to MP."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - } - if (player.getHpMpApUsed() < 1) { - c.getPlayer().message("You don't have enough HPMP stat points to spend on AP Reset."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - - int hp = player.getMaxHp(); - int level_ = player.getLevel(); - - boolean canWash_ = true; - if (hp < level_ * 14 + 148) { - canWash_ = false; - } - - if (!canWash_) { - c.getPlayer().message("You don't have the minimum HP pool required to swap."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - - player.setHpMpApUsed(player.getHpMpApUsed() - 1); - int hplose = -DistributeAPHandler.takeHp(player, player.getJob()); - int nextHp = Math.max(1, player.getHp() + hplose), nextMaxHp = Math.max(50, player.getMaxHp() + hplose); - - player.setHp(nextHp); - player.setMaxHp(nextMaxHp); - statupdate.add(new Pair<>(MapleStat.HP, nextHp)); - statupdate.add(new Pair<>(MapleStat.MAXHP, nextMaxHp)); - - break; - case 8192: // MP - if(ServerConstants.USE_ENFORCE_HPMP_SWAP) { - if (APTo != 2048) { - c.getPlayer().message("You can only swap MP ability points to HP."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - } - if (player.getHpMpApUsed() < 1) { - c.getPlayer().message("You don't have enough HPMP stat points to spend on AP Reset."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - - int mp = player.getMaxMp(); - int level = player.getLevel(); - MapleJob job = player.getJob(); - - boolean canWash = true; - if (job.isA(MapleJob.SPEARMAN) && mp < 4 * level + 156) { - canWash = false; - } else if (job.isA(MapleJob.FIGHTER) && mp < 4 * level + 56) { - canWash = false; - } else if (job.isA(MapleJob.THIEF) && job.getId() % 100 > 0 && mp < level * 14 - 4) { - canWash = false; - } else if (mp < level * 14 + 148) { - canWash = false; - } - - if (!canWash) { - c.getPlayer().message("You don't have the minimum MP pool required to swap."); - c.announce(MaplePacketCreator.enableActions()); - return; - } - - player.setHpMpApUsed(player.getHpMpApUsed() - 1); - int mplose = -DistributeAPHandler.takeMp(player, job); - int nextMp = Math.max(0, player.getMp() + mplose), nextMaxMp = Math.max(5, player.getMaxMp() + mplose); - - player.setMp(nextMp); - player.setMaxMp(nextMaxMp); - statupdate.add(new Pair<>(MapleStat.MP, nextMp)); - statupdate.add(new Pair<>(MapleStat.MAXMP, nextMaxMp)); - - break; - default: - c.announce(MaplePacketCreator.updatePlayerStats(MaplePacketCreator.EMPTY_STATUPDATE, true, c.getPlayer())); - return; + + if(!AssignAPProcessor.APResetAction(c, APFrom, APTo)) { + return; } - DistributeAPHandler.addStat(c, APTo, true); - c.announce(MaplePacketCreator.updatePlayerStats(statupdate, true, c.getPlayer())); } remove(c, itemId); } else if (itemType == 506) { @@ -287,7 +167,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { eq.setOwner(player.getName()); } else if (itemId == 5060001 || itemId == 5061000 || itemId == 5061001 || itemId == 5061002 || itemId == 5061003) { // Sealing lock MapleInventoryType type = MapleInventoryType.getByType((byte) slea.readInt()); - eq = c.getPlayer().getInventory(type).getItem((short) slea.readInt()); + eq = player.getInventory(type).getItem((short) slea.readInt()); if (eq == null) { //Check if the type is EQUIPMENT? return; } @@ -317,7 +197,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } else if (itemId == 5060002) { // Incubator byte inventory2 = (byte) slea.readInt(); short slot2 = (short) slea.readInt(); - Item item2 = c.getPlayer().getInventory(MapleInventoryType.getByType(inventory2)).getItem(slot2); + Item item2 = player.getInventory(MapleInventoryType.getByType(inventory2)).getItem(slot2); if (item2 == null) // hacking { return; @@ -388,11 +268,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { break; case 6: //item megaphone - String msg = medal + c.getPlayer().getName() + " : " + slea.readMapleAsciiString(); + String msg = medal + player.getName() + " : " + slea.readMapleAsciiString(); whisper = slea.readByte() == 1; Item item = null; if (slea.readByte() == 1) { //item - item = c.getPlayer().getInventory(MapleInventoryType.getByType((byte) slea.readInt())).getItem((short) slea.readInt()); + item = player.getInventory(MapleInventoryType.getByType((byte) slea.readInt())).getItem((short) slea.readInt()); if (item == null) //hack { return; @@ -412,7 +292,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } String[] msg2 = new String[lines]; for (int i = 0; i < lines; i++) { - msg2[i] = medal + c.getPlayer().getName() + " : " + slea.readMapleAsciiString(); + msg2[i] = medal + player.getName() + " : " + slea.readMapleAsciiString(); } whisper = slea.readByte() == 1; Server.getInstance().broadcastMessage(c.getWorld(), MaplePacketCreator.getMultiMegaphone(msg2, c.getChannel(), whisper)); @@ -420,10 +300,10 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } remove(c, itemId); } else if (itemType == 508) { // graduation banner, thanks to tmskdl12 - MapleKite kite = new MapleKite(c.getPlayer(), slea.readMapleAsciiString(), itemId); + MapleKite kite = new MapleKite(player, slea.readMapleAsciiString(), itemId); - if (!GameConstants.isFreeMarketRoom(c.getPlayer().getMapId())) { - c.getPlayer().getMap().spawnKite(kite); + if (!GameConstants.isFreeMarketRoom(player.getMapId())) { + player.getMap().spawnKite(kite); remove(c, itemId); } else { c.announce(MaplePacketCreator.sendCannotSpawnKite()); @@ -442,11 +322,11 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { remove(c, itemId); } else if (itemType == 512) { if (ii.getStateChangeItem(itemId) != 0) { - for (MapleCharacter mChar : c.getPlayer().getMap().getCharacters()) { + for (MapleCharacter mChar : player.getMap().getCharacters()) { ii.getItemEffect(ii.getStateChangeItem(itemId)).applyTo(mChar); } } - player.getMap().startMapEffect(ii.getMsg(itemId).replaceFirst("%s", c.getPlayer().getName()).replaceFirst("%s", slea.readMapleAsciiString()), itemId); + player.getMap().startMapEffect(ii.getMsg(itemId).replaceFirst("%s", player.getName()).replaceFirst("%s", slea.readMapleAsciiString()), itemId); remove(c, itemId); } else if (itemType == 517) { MaplePet pet = player.getPet(0); @@ -512,7 +392,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } final int world = c.getWorld(); - Server.getInstance().broadcastMessage(world, MaplePacketCreator.getAvatarMega(c.getPlayer(), medal, c.getChannel(), itemId, strLines, (slea.readByte() != 0))); + Server.getInstance().broadcastMessage(world, MaplePacketCreator.getAvatarMega(player, medal, c.getChannel(), itemId, strLines, (slea.readByte() != 0))); TimerManager.getInstance().schedule(new Runnable() { @Override public void run() { @@ -585,20 +465,20 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } else if (itemType == 552) { MapleInventoryType type = MapleInventoryType.getByType((byte) slea.readInt()); short slot = (short) slea.readInt(); - Item item = c.getPlayer().getInventory(type).getItem(slot); + Item item = player.getInventory(type).getItem(slot); if (item == null || item.getQuantity() <= 0 || MapleKarmaManipulator.hasKarmaFlag(item) || !ii.isKarmaAble(item.getItemId())) { c.announce(MaplePacketCreator.enableActions()); return; } if(MapleKarmaManipulator.hasUsedKarmaFlag(item)) { - c.getPlayer().dropMessage(6, "Scissors of Karma was already used on this item."); + player.dropMessage(6, "Scissors of Karma was already used on this item."); c.announce(MaplePacketCreator.enableActions()); return; } MapleKarmaManipulator.setKarmaFlag(item); - c.getPlayer().forceUpdateItem(item); + player.forceUpdateItem(item); remove(c, itemId); c.announce(MaplePacketCreator.enableActions()); } else if (itemType == 552) { //DS EGG THING @@ -607,8 +487,8 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { slea.readInt(); int itemSlot = slea.readInt(); slea.readInt(); - final Equip equip = (Equip) c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem((short) itemSlot); - if (equip.getVicious() == 2 || c.getPlayer().getInventory(MapleInventoryType.CASH).findById(5570000) == null) { + final Equip equip = (Equip) player.getInventory(MapleInventoryType.EQUIP).getItem((short) itemSlot); + if (equip.getVicious() == 2 || player.getInventory(MapleInventoryType.CASH).findById(5570000) == null) { return; } equip.setVicious(equip.getVicious() + 1); @@ -623,14 +503,14 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { } final byte eSlot = (byte) slea.readInt(); - final Item eitem = c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem(eSlot); + final Item eitem = player.getInventory(MapleInventoryType.EQUIP).getItem(eSlot); if (slea.readInt() != 2) { return; } final byte uSlot = (byte) slea.readInt(); - final Item uitem = c.getPlayer().getInventory(MapleInventoryType.USE).getItem(uSlot); + final Item uitem = player.getInventory(MapleInventoryType.USE).getItem(uSlot); if (eitem == null || uitem == null) { return; } @@ -644,12 +524,12 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler { //should have a check here against PE hacks if(itemId / 1000000 != 5) itemId = 0; - c.getPlayer().toggleBlockCashShop(); + player.toggleBlockCashShop(); final int curlevel = toScroll.getLevel(); c.getSession().write(MaplePacketCreator.sendVegaScroll(0x40)); - final Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, uitem.getItemId(), false, itemId, c.getPlayer().isGM()); + final Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, uitem.getItemId(), false, itemId, player.isGM()); c.getSession().write(MaplePacketCreator.sendVegaScroll(scrolled.getLevel() > curlevel ? 0x41 : 0x43)); //opcodes 0x42, 0x44: "this item cannot be used"; 0x39, 0x45: crashes diff --git a/src/net/server/channel/worker/BaseScheduler.java b/src/net/server/channel/worker/BaseScheduler.java index e4cec03f58..a95abdf053 100644 --- a/src/net/server/channel/worker/BaseScheduler.java +++ b/src/net/server/channel/worker/BaseScheduler.java @@ -27,11 +27,11 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.locks.Lock; import net.server.Server; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import server.TimerManager; import tools.Pair; @@ -42,7 +42,7 @@ import tools.Pair; public abstract class BaseScheduler { private int idleProcs = 0; private List listeners = new LinkedList<>(); - private final List externalLocks = new LinkedList<>(); + private final List externalLocks = new LinkedList<>(); private Map> registeredEntries = new HashMap<>(); private ScheduledFuture schedulerTask = null; @@ -55,14 +55,14 @@ public abstract class BaseScheduler { }; protected BaseScheduler(MonitoredLockType lockType) { - schedulerLock = new MonitoredReentrantLock(lockType, true); + schedulerLock = MonitoredReentrantLockFactory.createLock(lockType, true); } // NOTE: practice EXTREME caution when adding external locks to the scheduler system, if you don't know what you're doing DON'T USE THIS. - protected BaseScheduler(MonitoredLockType lockType, List extLocks) { - schedulerLock = new MonitoredReentrantLock(lockType, true); + protected BaseScheduler(MonitoredLockType lockType, List extLocks) { + schedulerLock = MonitoredReentrantLockFactory.createLock(lockType, true); - for(Lock lock : extLocks) { + for(MonitoredReentrantLock lock : extLocks) { externalLocks.add(lock); } } @@ -73,7 +73,7 @@ public abstract class BaseScheduler { private void lockScheduler() { if(!externalLocks.isEmpty()) { - for(Lock l : externalLocks) { + for(MonitoredReentrantLock l : externalLocks) { l.lock(); } } @@ -83,7 +83,7 @@ public abstract class BaseScheduler { private void unlockScheduler() { if(!externalLocks.isEmpty()) { - for(Lock l : externalLocks) { + for(MonitoredReentrantLock l : externalLocks) { l.unlock(); } } @@ -190,12 +190,12 @@ public abstract class BaseScheduler { } listeners.clear(); - externalLocks.clear(); registeredEntries.clear(); } finally { unlockScheduler(); + externalLocks.clear(); } - schedulerLock.dispose(); + schedulerLock = schedulerLock.dispose(); } } diff --git a/src/net/server/channel/worker/FaceExpressionScheduler.java b/src/net/server/channel/worker/FaceExpressionScheduler.java index ffba7a2c4f..2cff3228b0 100644 --- a/src/net/server/channel/worker/FaceExpressionScheduler.java +++ b/src/net/server/channel/worker/FaceExpressionScheduler.java @@ -20,15 +20,15 @@ package net.server.channel.worker; import java.util.Collections; -import java.util.concurrent.locks.Lock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantLock; /** * * @author Ronan */ public class FaceExpressionScheduler extends BaseScheduler { - public FaceExpressionScheduler(final Lock channelFaceLock) { + public FaceExpressionScheduler(final MonitoredReentrantLock channelFaceLock) { super(MonitoredLockType.CHANNEL_FACESCHDL, Collections.singletonList(channelFaceLock)); } diff --git a/src/net/server/channel/worker/MobAnimationScheduler.java b/src/net/server/channel/worker/MobAnimationScheduler.java index e615fcf9d9..cead942343 100644 --- a/src/net/server/channel/worker/MobAnimationScheduler.java +++ b/src/net/server/channel/worker/MobAnimationScheduler.java @@ -25,6 +25,7 @@ import java.util.HashSet; import java.util.List; import java.util.Set; import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -32,7 +33,7 @@ import net.server.audit.locks.MonitoredReentrantLock; */ public class MobAnimationScheduler extends BaseScheduler { Set onAnimationMobs = new HashSet<>(1000); - private MonitoredReentrantLock animationLock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_MOBANIMAT, true); + private MonitoredReentrantLock animationLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_MOBANIMAT, true); private static Runnable r = new Runnable() { @Override @@ -75,7 +76,7 @@ public class MobAnimationScheduler extends BaseScheduler { @Override public void dispose() { - animationLock.dispose(); + animationLock = animationLock.dispose(); super.dispose(); } } diff --git a/src/net/server/channel/worker/MobStatusScheduler.java b/src/net/server/channel/worker/MobStatusScheduler.java index 041079c87b..702370ebb3 100644 --- a/src/net/server/channel/worker/MobStatusScheduler.java +++ b/src/net/server/channel/worker/MobStatusScheduler.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -34,7 +35,7 @@ import net.server.audit.locks.MonitoredReentrantLock; */ public class MobStatusScheduler extends BaseScheduler { private Map registeredMobStatusOvertime = new HashMap<>(); - private MonitoredReentrantLock overtimeStatusLock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL_OVTSTATUS, true); + private MonitoredReentrantLock overtimeStatusLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHANNEL_OVTSTATUS, true); private class MobStatusOvertimeEntry { private int procCount; @@ -110,7 +111,7 @@ public class MobStatusScheduler extends BaseScheduler { @Override public void dispose() { - overtimeStatusLock.dispose(); + overtimeStatusLock = overtimeStatusLock.dispose(); super.dispose(); } } diff --git a/src/net/server/guild/MapleGuild.java b/src/net/server/guild/MapleGuild.java index 90709b8df9..f5039a0438 100644 --- a/src/net/server/guild/MapleGuild.java +++ b/src/net/server/guild/MapleGuild.java @@ -36,7 +36,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.Server; import net.server.channel.Channel; @@ -51,7 +51,7 @@ public class MapleGuild { } private final List members; - private final Lock membersLock = new MonitoredReentrantLock(MonitoredLockType.GUILD, true); + private final Lock membersLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.GUILD, true); private String rankTitles[] = new String[5]; // 1 = master, 2 = jr, 5 = lowest member private String name, notice; diff --git a/src/net/server/world/MapleParty.java b/src/net/server/world/MapleParty.java index f5dd1467dc..ac93f14522 100644 --- a/src/net/server/world/MapleParty.java +++ b/src/net/server/world/MapleParty.java @@ -32,6 +32,7 @@ import java.util.Map; import java.util.Comparator; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; public class MapleParty { private int id; @@ -43,7 +44,7 @@ public class MapleParty { private Map histMembers = new HashMap<>(); private int nextEntry = 0; - private MonitoredReentrantLock lock = new MonitoredReentrantLock(MonitoredLockType.PARTY, true); + private MonitoredReentrantLock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.PARTY, true); public MapleParty(int id, MaplePartyCharacter chrfor) { this.leaderId = chrfor.getId(); @@ -215,7 +216,7 @@ public class MapleParty { } public void disposeLocks() { - lock.dispose(); + lock = lock.dispose(); } @Override diff --git a/src/net/server/world/World.java b/src/net/server/world/World.java index 4fba8756ed..8583bde942 100644 --- a/src/net/server/world/World.java +++ b/src/net/server/world/World.java @@ -47,11 +47,13 @@ import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; import java.util.concurrent.atomic.AtomicInteger; -import net.server.audit.locks.MonitoredReentrantLock; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; + import java.util.Set; import java.util.HashSet; import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.locks.ReentrantReadWriteLock; import scripting.event.EventInstanceManager; import server.TimerManager; @@ -76,7 +78,9 @@ import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.Pair; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -99,11 +103,11 @@ public class World { private PlayerStorage players = new PlayerStorage(); private final ReentrantReadWriteLock chnLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_CHANNELS, true); - private final ReentrantReadWriteLock.ReadLock chnRLock = chnLock.readLock(); - private final ReentrantReadWriteLock.WriteLock chnWLock = chnLock.writeLock(); + private ReadLock chnRLock = chnLock.readLock(); + private WriteLock chnWLock = chnLock.writeLock(); private Map> accountChars = new HashMap<>(); - private MonitoredReentrantLock accountCharsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_CHARS, true); + private MonitoredReentrantLock accountCharsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_CHARS, true); private Set queuedGuilds = new HashSet<>(); private Map, Pair>> queuedMarriages = new HashMap<>(); @@ -112,31 +116,31 @@ public class World { private Map partyChars = new HashMap<>(); private Map parties = new HashMap<>(); private AtomicInteger runningPartyId = new AtomicInteger(); - private MonitoredReentrantLock partyLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PARTY, true); + private MonitoredReentrantLock partyLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_PARTY, true); private Map owlSearched = new LinkedHashMap<>(); - private MonitoredReentrantLock owlLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_OWL); + private MonitoredReentrantLock owlLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_OWL); - private MonitoredReentrantLock activePetsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PETS, true); + private MonitoredReentrantLock activePetsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_PETS, true); private Map activePets = new LinkedHashMap<>(); private ScheduledFuture petsSchedule; private long petUpdate; - private MonitoredReentrantLock activeMountsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MOUNTS, true); + private MonitoredReentrantLock activeMountsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_MOUNTS, true); private Map activeMounts = new LinkedHashMap<>(); private ScheduledFuture mountsSchedule; private long mountUpdate; - private MonitoredReentrantLock activePlayerShopsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_PSHOPS, true); + private MonitoredReentrantLock activePlayerShopsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_PSHOPS, true); private Map activePlayerShops = new LinkedHashMap<>(); - private MonitoredReentrantLock activeMerchantsLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MERCHS, true); + private MonitoredReentrantLock activeMerchantsLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_MERCHS, true); private Map> activeMerchants = new LinkedHashMap<>(); private long merchantUpdate; private Map registeredTimedMapObjects = new LinkedHashMap<>(); private ScheduledFuture timedMapObjectsSchedule; - private MonitoredReentrantLock timedMapObjectLock = new MonitoredReentrantLock(MonitoredLockType.WORLD_MAPOBJS, true); + private MonitoredReentrantLock timedMapObjectLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_MAPOBJS, true); private ScheduledFuture charactersSchedule; private ScheduledFuture marriagesSchedule; @@ -216,7 +220,7 @@ public class World { chnRLock.unlock(); } - if(ch == null || ch.getPlayerStorage().getSize() > 0) { + if(ch == null || !ch.canUninstall()) { return -1; } @@ -235,6 +239,18 @@ public class World { return ch.getId(); } + public boolean canUninstall() { + if(players.getSize() > 0) return false; + + for(Channel ch : this.getChannels()) { + if(!ch.canUninstall()) { + return false; + } + } + + return true; + } + public void setFlag(byte b) { this.flag = b; } @@ -401,11 +417,17 @@ public class World { return players; } + public void addPlayer(MapleCharacter chr) { + players.addPlayer(chr); + } + public void removePlayer(MapleCharacter chr) { - if(!getChannel(chr.getClient().getChannel()).removePlayer(chr)) { - if(!chr.getClient().getChannelServer().removePlayer(chr)) { + Channel cserv = chr.getClient().getChannelServer(); + + if(cserv != null) { + if(!cserv.removePlayer(chr)) { // oy the player is not where it should be, find this mf - + for(Channel ch : getChannels()) { if(ch.removePlayer(chr)) { break; @@ -1661,14 +1683,14 @@ public class World { p.disposeLocks(); } - accountCharsLock.dispose(); - partyLock.dispose(); - owlLock.dispose(); - activePetsLock.dispose(); - activeMountsLock.dispose(); - activePlayerShopsLock.dispose(); - activeMerchantsLock.dispose(); - timedMapObjectLock.dispose(); + accountCharsLock = accountCharsLock.dispose(); + partyLock = partyLock.dispose(); + owlLock = owlLock.dispose(); + activePetsLock = activePetsLock.dispose(); + activeMountsLock = activeMountsLock.dispose(); + activePlayerShopsLock = activePlayerShopsLock.dispose(); + activeMerchantsLock = activeMerchantsLock.dispose(); + timedMapObjectLock = timedMapObjectLock.dispose(); } public final void shutdown() { @@ -1702,6 +1724,8 @@ public class World { } players.disconnectAll(); + players = null; + disposeLocks(); } } diff --git a/src/scripting/event/EventInstanceManager.java b/src/scripting/event/EventInstanceManager.java index 422ffe5164..4bde63d243 100644 --- a/src/scripting/event/EventInstanceManager.java +++ b/src/scripting/event/EventInstanceManager.java @@ -34,13 +34,11 @@ import java.util.HashSet; import java.util.Set; import java.util.Iterator; import java.util.Properties; -import java.util.concurrent.locks.ReentrantReadWriteLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; -import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import javax.script.ScriptException; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.world.MapleParty; import net.server.world.MaplePartyCharacter; import provider.MapleDataProviderFactory; @@ -61,6 +59,9 @@ import constants.ServerConstants; import java.awt.Point; import java.sql.Connection; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.locks.ReentrantReadWriteLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; +import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; import java.util.logging.Level; import java.util.logging.Logger; import scripting.AbstractPlayerInteraction; @@ -89,11 +90,11 @@ public class EventInstanceManager { private List mapIds = new LinkedList<>(); private final ReentrantReadWriteLock lock = new MonitoredReentrantReadWriteLock(MonitoredLockType.EIM, true); - private final ReadLock rL = lock.readLock(); - private final WriteLock wL = lock.writeLock(); + private ReadLock rL = lock.readLock(); + private WriteLock wL = lock.writeLock(); - private final MonitoredReentrantLock pL = new MonitoredReentrantLock(MonitoredLockType.EIM_PARTY, true); - private final MonitoredReentrantLock sL = new MonitoredReentrantLock(MonitoredLockType.EIM_SCRIPT, true); + private MonitoredReentrantLock pL = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EIM_PARTY, true); + private MonitoredReentrantLock sL = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EIM_SCRIPT, true); private ScheduledFuture event_schedule = null; private boolean disposed = false; @@ -467,7 +468,7 @@ public class EventInstanceManager { } } - public void changedMap(MapleCharacter chr, int mapId) { // optional + public void changedMap(MapleCharacter chr, int mapId) { try { sL.lock(); try { @@ -475,10 +476,10 @@ public class EventInstanceManager { } finally { sL.unlock(); } - } catch (ScriptException | NoSuchMethodException ex) {} + } catch (ScriptException | NoSuchMethodException ex) {} // optional } - public void afterChangedMap(MapleCharacter chr, int mapId) { // optional + public void afterChangedMap(MapleCharacter chr, int mapId) { try { sL.lock(); try { @@ -486,7 +487,7 @@ public class EventInstanceManager { } finally { sL.unlock(); } - } catch (ScriptException | NoSuchMethodException ex) {} + } catch (ScriptException | NoSuchMethodException ex) {} // optional } public void changedLeader(MapleCharacter ldr) { @@ -537,9 +538,7 @@ public class EventInstanceManager { } finally { sL.unlock(); } - } catch (ScriptException | NoSuchMethodException ex) { - //optional - } + } catch (ScriptException | NoSuchMethodException ex) {} //optional } public void playerKilled(MapleCharacter chr) { @@ -563,9 +562,7 @@ public class EventInstanceManager { } finally { sL.unlock(); } - } catch (ScriptException | NoSuchMethodException ex) { - ex.printStackTrace(); - } + } catch (ScriptException | NoSuchMethodException ex) {} // optional } public boolean revivePlayer(MapleCharacter chr) { @@ -637,7 +634,18 @@ public class EventInstanceManager { return (kc == null) ? 0 : kc; } - public synchronized void dispose() { + public void dispose() { + Thread t = new Thread( new Runnable() { + @Override + public void run() { + dispose(false); + } + }); + + t.start(); + } + + public synchronized void dispose(boolean shutdown) { if(disposed) return; disposed = true; @@ -688,8 +696,8 @@ public class EventInstanceManager { } private void disposeLocks() { - pL.dispose(); - sL.dispose(); + pL = pL.dispose(); + sL = sL.dispose(); } public MapleMapFactory getMapFactory() { diff --git a/src/scripting/event/EventManager.java b/src/scripting/event/EventManager.java index 7e7ac7157e..efa760bea4 100644 --- a/src/scripting/event/EventManager.java +++ b/src/scripting/event/EventManager.java @@ -54,6 +54,7 @@ import java.util.LinkedList; import java.util.Queue; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -75,8 +76,8 @@ public class EventManager { private Integer readyId = 0; private Properties props = new Properties(); private String name; - private MonitoredReentrantLock lobbyLock = new MonitoredReentrantLock(MonitoredLockType.EM_LOBBY); - private MonitoredReentrantLock queueLock = new MonitoredReentrantLock(MonitoredLockType.EM_QUEUE); + private MonitoredReentrantLock lobbyLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EM_LOBBY); + private MonitoredReentrantLock queueLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EM_QUEUE); private static final int maxLobbys = 8; // an event manager holds up to this amount of concurrent lobbys @@ -102,7 +103,7 @@ public class EventManager { synchronized(instances) { for(EventInstanceManager eim : instances.values()) { - eim.dispose(); + eim.dispose(true); } instances.clear(); } @@ -117,7 +118,7 @@ public class EventManager { } for(EventInstanceManager eim : readyEims) { - eim.dispose(); + eim.dispose(true); } props.clear(); @@ -130,8 +131,8 @@ public class EventManager { } private void disposeLocks() { - lobbyLock.dispose(); - queueLock.dispose(); + lobbyLock = lobbyLock.dispose(); + queueLock = queueLock.dispose(); } private static List convertToIntegerArray(List list) { diff --git a/src/scripting/event/worker/EventScriptScheduler.java b/src/scripting/event/worker/EventScriptScheduler.java index c26cd24091..974be7c593 100644 --- a/src/scripting/event/worker/EventScriptScheduler.java +++ b/src/scripting/event/worker/EventScriptScheduler.java @@ -30,6 +30,7 @@ import server.TimerManager; import net.server.Server; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -50,7 +51,7 @@ public class EventScriptScheduler { }; public EventScriptScheduler() { - schedulerLock = new MonitoredReentrantLock(MonitoredLockType.EM_SCHDL, true); + schedulerLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.EM_SCHDL, true); } private void runBaseSchedule() { @@ -160,7 +161,7 @@ public class EventScriptScheduler { schedulerLock.unlock(); } - schedulerLock.dispose(); + schedulerLock = schedulerLock.dispose(); } }); diff --git a/src/server/CashShop.java b/src/server/CashShop.java index 42fd4da30a..de1e8a2ab8 100644 --- a/src/server/CashShop.java +++ b/src/server/CashShop.java @@ -32,7 +32,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import provider.MapleData; import provider.MapleDataProvider; @@ -269,7 +269,7 @@ public class CashShop { private List inventory = new ArrayList<>(); private List wishList = new ArrayList<>(); private int notes = 0; - private Lock lock = new MonitoredReentrantLock(MonitoredLockType.CASHSHOP); + private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CASHSHOP); public CashShop(int accountId, int characterId, int jobType) throws SQLException { this.accountId = accountId; diff --git a/src/server/MapleStorage.java b/src/server/MapleStorage.java index b2e80337e9..18d8c0edbb 100644 --- a/src/server/MapleStorage.java +++ b/src/server/MapleStorage.java @@ -39,7 +39,7 @@ import provider.MapleData; import provider.MapleDataProvider; import provider.MapleDataProviderFactory; import provider.MapleDataTool; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import tools.DatabaseConnection; import tools.MaplePacketCreator; import tools.Pair; @@ -59,7 +59,7 @@ public class MapleStorage { private byte slots; private Map> typeItems = new HashMap<>(); private List items; - private Lock lock = new MonitoredReentrantLock(MonitoredLockType.STORAGE, true); + private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.STORAGE, true); private MapleStorage(int id, byte slots, int meso) { this.id = id; diff --git a/src/server/life/MapleMonster.java b/src/server/life/MapleMonster.java index 9601cc9b41..d6b0111629 100644 --- a/src/server/life/MapleMonster.java +++ b/src/server/life/MapleMonster.java @@ -70,6 +70,7 @@ import tools.MaplePacketCreator; import tools.Pair; import tools.Randomizer; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; public class MapleMonster extends AbstractLoadedMapleLife { private ChangeableStats ostats = null; //unused, v83 WZs offers no support for changeable stats. @@ -94,10 +95,10 @@ public class MapleMonster extends AbstractLoadedMapleLife { private int parentMobOid = 0; private final HashMap takenDamage = new HashMap<>(); - private MonitoredReentrantLock externalLock = new MonitoredReentrantLock(MonitoredLockType.MOB_EXT); - private MonitoredReentrantLock monsterLock = new MonitoredReentrantLock(MonitoredLockType.MOB, true); - private MonitoredReentrantLock statiLock = new MonitoredReentrantLock(MonitoredLockType.MOB_STATI); - private MonitoredReentrantLock animationLock = new MonitoredReentrantLock(MonitoredLockType.MOB_ANI); + private MonitoredReentrantLock externalLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_EXT); + private MonitoredReentrantLock monsterLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB, true); + private MonitoredReentrantLock statiLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_STATI); + private MonitoredReentrantLock animationLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MOB_ANI); public MapleMonster(int id, MapleMonsterStats stats) { super(id); @@ -1477,9 +1478,9 @@ public class MapleMonster extends AbstractLoadedMapleLife { } public final void disposeLocks() { - externalLock.dispose(); - monsterLock.dispose(); - statiLock.dispose(); - animationLock.dispose(); + externalLock = externalLock.dispose(); + monsterLock = monsterLock.dispose(); + statiLock = statiLock.dispose(); + animationLock = animationLock.dispose(); } } diff --git a/src/server/maps/MapleGenericPortal.java b/src/server/maps/MapleGenericPortal.java index 957d8c0ac0..7ba6fe6b9d 100644 --- a/src/server/maps/MapleGenericPortal.java +++ b/src/server/maps/MapleGenericPortal.java @@ -26,9 +26,9 @@ import java.awt.Point; import scripting.portal.PortalScriptManager; import server.MaplePortal; import tools.MaplePacketCreator; -import java.util.concurrent.locks.Lock; import net.server.audit.locks.MonitoredLockType; import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; public class MapleGenericPortal implements MaplePortal { @@ -41,7 +41,7 @@ public class MapleGenericPortal implements MaplePortal { private int id; private String scriptName; private boolean portalState; - private Lock scriptLock = null; + private MonitoredReentrantLock scriptLock = null; public MapleGenericPortal(int type) { this.type = type; @@ -118,7 +118,7 @@ public class MapleGenericPortal implements MaplePortal { if(scriptName != null) { if(scriptLock == null) { - scriptLock = new MonitoredReentrantLock(MonitoredLockType.PORTAL, true); + scriptLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.PORTAL, true); } } else { scriptLock = null; diff --git a/src/server/maps/MapleHiredMerchant.java b/src/server/maps/MapleHiredMerchant.java index 8f9a366fda..dd0819f966 100644 --- a/src/server/maps/MapleHiredMerchant.java +++ b/src/server/maps/MapleHiredMerchant.java @@ -40,7 +40,7 @@ import java.util.LinkedList; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import net.server.Server; import server.MapleItemInformationProvider; import tools.DatabaseConnection; @@ -65,7 +65,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject { private List sold = new LinkedList<>(); private AtomicBoolean open = new AtomicBoolean(); private MapleMap map; - private Lock visitorLock = new MonitoredReentrantLock(MonitoredLockType.VISITOR_MERCH, true); + private Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_MERCH, true); public MapleHiredMerchant(final MapleCharacter owner, String desc, int itemId) { this.setPosition(owner.getPosition()); diff --git a/src/server/maps/MapleMap.java b/src/server/maps/MapleMap.java index 54bc8fd6dd..6677269ee9 100644 --- a/src/server/maps/MapleMap.java +++ b/src/server/maps/MapleMap.java @@ -43,6 +43,7 @@ import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.HashSet; import java.util.Set; import java.util.LinkedList; import java.util.List; @@ -53,12 +54,12 @@ import java.util.Random; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredLockType; -import net.server.audit.locks.MonitoredReentrantLock; -import net.server.audit.locks.MonitoredReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock; import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock; +import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.MonitoredReentrantReadWriteLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import java.lang.ref.WeakReference; import net.server.Server; import net.server.channel.Channel; @@ -108,6 +109,7 @@ public class MapleMap { private Map environment = new LinkedHashMap<>(); private Map droppedItems = new LinkedHashMap<>(); private LinkedList> registeredDrops = new LinkedList<>(); + private Map mobLootEntries = new HashMap(20); private List areas = new ArrayList<>(); private MapleFootholdTree footholds = null; private Pair xLimits; // caches the min and max x's with available footholds @@ -141,11 +143,11 @@ public class MapleMap { private ScheduledFuture mapMonitor = null; private ScheduledFuture itemMonitor = null; private ScheduledFuture expireItemsTask = null; + private ScheduledFuture mobSpawnLootTask = null; private short itemMonitorTimeout; private Pair timeMob = null; private short mobInterval = 5000; private boolean allowSummons = true; // All maps should have this true at the beginning - private int lastDoorOwner = -1; // HPQ private int riceCakes = 0; @@ -157,13 +159,15 @@ public class MapleMap { private MapleCoconut coconut; //locks - private final ReadLock chrRLock; - private final WriteLock chrWLock; - private final ReadLock objectRLock; - private final WriteLock objectWLock; + private ReadLock chrRLock; + private WriteLock chrWLock; + private ReadLock objectRLock; + private WriteLock objectWLock; + private Lock lootLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_LOOT, true); + // due to the nature of loadMapFromWz (synchronized), sole function that calls 'generateMapDropRangeCache', this lock remains optional. - private static final Lock bndLock = new MonitoredReentrantLock(MonitoredLockType.MAP_BOUNDS, true); + private static final Lock bndLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_BOUNDS, true); public MapleMap(int mapid, int world, int channel, int returnMapId, float monsterRate) { this.mapid = mapid; @@ -695,22 +699,14 @@ public class MapleMap { if(useBaseRate) chRate = 1; final MapleMonsterInformationProvider mi = MapleMonsterInformationProvider.getInstance(); + final List globalEntry = mi.getGlobalDrop(); final List dropEntry = new ArrayList<>(); final List visibleQuestEntry = new ArrayList<>(); final List otherQuestEntry = new ArrayList<>(); sortDropEntries(mi.retrieveEffectiveDrop(mob.getId()), dropEntry, visibleQuestEntry, otherQuestEntry, chr); - // Normal Drops - d = dropItemsFromMonsterOnMap(dropEntry, pos, d, chRate, droptype, mobpos, chr, mob); - - // Global Drops - final List globalEntry = mi.getGlobalDrop(); - d = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, d, droptype, mobpos, chr, mob); - - // Quest Drops - d = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob); - dropItemsFromMonsterOnMap(otherQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob); + registerMobItemDrops(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob); } public void dropItemsFromMonster(List list, final MapleCharacter chr, final MapleMonster mob) { @@ -736,15 +732,15 @@ public class MapleMap { } private void stopItemMonitor() { - chrWLock.lock(); - try { - itemMonitor.cancel(false); - itemMonitor = null; - - expireItemsTask.cancel(false); - expireItemsTask = null; - } finally { - chrWLock.unlock(); + itemMonitor.cancel(false); + itemMonitor = null; + + expireItemsTask.cancel(false); + expireItemsTask = null; + + if(ServerConstants.USE_SPAWN_LOOT_ON_ANIMATION) { + mobSpawnLootTask.cancel(false); + mobSpawnLootTask = null; } } @@ -765,18 +761,34 @@ public class MapleMap { itemMonitor = TimerManager.getInstance().register(new Runnable() { @Override public void run() { - if (getCharacters().isEmpty()) { - if(itemMonitorTimeout == 0) { - stopItemMonitor(); - return; + chrWLock.lock(); + try { + if (characters.isEmpty()) { + if(itemMonitorTimeout == 0) { + if(itemMonitor != null) { + stopItemMonitor(); + } + + return; + } else { + itemMonitorTimeout--; + } } else { - itemMonitorTimeout--; + itemMonitorTimeout = 1; } - } else { - itemMonitorTimeout = 1; + } finally { + chrWLock.unlock(); } - if(!registeredDrops.isEmpty()) cleanItemMonitor(); + boolean tryClean; + objectRLock.lock(); + try { + tryClean = registeredDrops.size() > 70; + } finally { + objectRLock.unlock(); + } + + if(tryClean) cleanItemMonitor(); } }, ServerConstants.ITEM_MONITOR_TIME, ServerConstants.ITEM_MONITOR_TIME); @@ -786,6 +798,22 @@ public class MapleMap { makeDisappearExpiredItemDrops(); } }, ServerConstants.ITEM_EXPIRE_CHECK, ServerConstants.ITEM_EXPIRE_CHECK); + + if(ServerConstants.USE_SPAWN_LOOT_ON_ANIMATION) { + lootLock.lock(); + try { + mobLootEntries.clear(); + } finally { + lootLock.unlock(); + } + + mobSpawnLootTask = TimerManager.getInstance().register(new Runnable() { + @Override + public void run() { + spawnMobItemDrops(); + } + }, 200, 200); + } itemMonitorTimeout = 1; } finally { @@ -878,6 +906,63 @@ public class MapleMap { } } + private void registerMobItemDrops(byte droptype, int mobpos, int chRate, Point pos, List dropEntry, List visibleQuestEntry, List otherQuestEntry, List globalEntry, MapleCharacter chr, MapleMonster mob) { + MobLootEntry mle = new MobLootEntry(droptype, mobpos, chRate, pos, dropEntry, visibleQuestEntry, otherQuestEntry, globalEntry, chr, mob); + + if(ServerConstants.USE_SPAWN_LOOT_ON_ANIMATION) { + int animationTime = mob.getAnimationTime("die1"); + + lootLock.lock(); + try { + long timeNow = Server.getInstance().getCurrentTime(); + mobLootEntries.put(mle, timeNow + ((long)(0.42 * animationTime))); + } finally { + lootLock.unlock(); + } + } else { + mle.run(); + } + } + + private void spawnMobItemDrops() { + Set> mleList; + + lootLock.lock(); + try { + mleList = new HashSet<>(mobLootEntries.entrySet()); + } finally { + lootLock.unlock(); + } + + long timeNow = Server.getInstance().getCurrentTime(); + List toRemove = new LinkedList<>(); + for(Entry mlee : mleList) { + if(mlee.getValue() < timeNow) { + toRemove.add(mlee.getKey()); + } + } + + if(!toRemove.isEmpty()) { + List toSpawnLoot = new LinkedList<>(); + + lootLock.lock(); + try { + for(MobLootEntry mle : toRemove) { + Long mler = mobLootEntries.remove(mle); + if(mler != null) { + toSpawnLoot.add(mle); + } + } + } finally { + lootLock.unlock(); + } + + for(MobLootEntry mle : toSpawnLoot) { + mle.run(); + } + } + } + private List getDroppedItems() { objectRLock.lock(); try { @@ -1748,6 +1833,7 @@ public class MapleMap { public void spawnRevives(final MapleMonster monster) { monster.setMap(this); + if(getEventInstance() != null) getEventInstance().registerMonster(monster); spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() { @Override @@ -1756,8 +1842,36 @@ public class MapleMap { c.announce(MaplePacketCreator.spawnMonster(monster, false)); } }); + updateMonsterController(monster); + + if (monster.hasBossHPBar()) { + broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition()); + } + spawnedMonstersOnMap.incrementAndGet(); + applyRemoveAfter(monster); + } + + private void applyRemoveAfter(final MapleMonster monster) { + final selfDestruction selfDestruction = monster.getStats().selfDestruction(); + if (monster.getStats().removeAfter() > 0 || selfDestruction != null && selfDestruction.getHp() < 0) { + if (selfDestruction == null) { + TimerManager.getInstance().schedule(new Runnable() { + @Override + public void run() { + killMonster(monster, null, false); + } + }, monster.getStats().removeAfter() * 1000); + } else { + TimerManager.getInstance().schedule(new Runnable() { + @Override + public void run() { + killMonster(monster, null, false, selfDestruction.getAction()); + } + }, selfDestruction.removeAfter() * 1000); + } + } } public void spawnAllMonsterIdFromMapSpawnList(int id) { @@ -1781,7 +1895,7 @@ public class MapleMap { spawnMonster(sp.getMonster(), difficulty, isPq); } } - + public void spawnMonster(final MapleMonster monster) { spawnMonster(monster, 1, false); } @@ -1804,6 +1918,10 @@ public class MapleMap { }, null); updateMonsterController(monster); + + if (monster.hasBossHPBar()) { + broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition()); + } if (monster.getDropPeriodTime() > 0) { //9300102 - Watchhog, 9300061 - Moon Bunny (HPQ), 9300093 - Tylus if (monster.getId() == 9300102) { @@ -1818,25 +1936,9 @@ public class MapleMap { FilePrinter.printError(FilePrinter.UNHANDLED_EVENT, "UNCODED TIMED MOB DETECTED: " + monster.getId() + "\r\n"); } } + spawnedMonstersOnMap.incrementAndGet(); - final selfDestruction selfDestruction = monster.getStats().selfDestruction(); - if (monster.getStats().removeAfter() > 0 || selfDestruction != null && selfDestruction.getHp() < 0) { - if (selfDestruction == null) { - TimerManager.getInstance().schedule(new Runnable() { - @Override - public void run() { - killMonster(monster, null, false); - } - }, monster.getStats().removeAfter() * 1000); - } else { - TimerManager.getInstance().schedule(new Runnable() { - @Override - public void run() { - killMonster(monster, null, false, selfDestruction.getAction()); - } - }, selfDestruction.removeAfter() * 1000); - } - } + applyRemoveAfter(monster); } public void spawnDojoMonster(final MapleMonster monster) { @@ -1850,6 +1952,8 @@ public class MapleMap { spos = calcPointBelow(spos); if(spos == null) return; + if(getEventInstance() != null) getEventInstance().registerMonster(monster); + spos.y--; monster.setPosition(spos); spawnAndAddRangedMapObject(monster, new DelayedPacketCreation() { @@ -1858,12 +1962,14 @@ public class MapleMap { c.announce(MaplePacketCreator.spawnMonster(monster, true, effect)); } }); + updateMonsterController(monster); + if (monster.hasBossHPBar()) { broadcastBossHpMessage(monster, monster.hashCode(), monster.makeBossHPBarPacket(), monster.getPosition()); } - updateMonsterController(monster); spawnedMonstersOnMap.incrementAndGet(); + applyRemoveAfter(monster); } public void spawnFakeMonster(final MapleMonster monster) { @@ -1929,21 +2035,19 @@ public class MapleMap { return chr.getMapId() == door.getFrom().getId(); } }); - - if(!door.inTown()) setLastDoorOwner(door.getOwnerId()); } public List getAvailableDoorPortals() { objectRLock.lock(); try { List availablePortals = new ArrayList<>(); - + for (MaplePortal port : portals.values()) { if (port.getType() == MaplePortal.DOOR_PORTAL) { availablePortals.add(port); } } - + return availablePortals; } finally { objectRLock.unlock(); @@ -2270,12 +2374,12 @@ public class MapleMap { chrSize = characters.size(); addPartyMemberInternal(chr); + itemMonitorTimeout = 1; } finally { chrWLock.unlock(); } chr.setMapId(mapid); - - itemMonitorTimeout = 1; + if (chrSize == 1) { if(!hasItemMonitor()) startItemMonitor(); @@ -3143,6 +3247,47 @@ public class MapleMap { return false; } + private class MobLootEntry implements Runnable { + private byte droptype; + private int mobpos; + private int chRate; + private Point pos; + private List dropEntry; + private List visibleQuestEntry; + private List otherQuestEntry; + private List globalEntry; + private MapleCharacter chr; + private MapleMonster mob; + + protected MobLootEntry(byte droptype, int mobpos, int chRate, Point pos, List dropEntry, List visibleQuestEntry, List otherQuestEntry, List globalEntry, MapleCharacter chr, MapleMonster mob) { + this.droptype = droptype; + this.mobpos = mobpos; + this.chRate = chRate; + this.pos = pos; + this.dropEntry = dropEntry; + this.visibleQuestEntry = visibleQuestEntry; + this.otherQuestEntry = otherQuestEntry; + this.globalEntry = globalEntry; + this.chr = chr; + this.mob = mob; + } + + @Override + public void run() { + byte d = 1; + + // Normal Drops + d = dropItemsFromMonsterOnMap(dropEntry, pos, d, chRate, droptype, mobpos, chr, mob); + + // Global Drops + d = dropGlobalItemsFromMonsterOnMap(globalEntry, pos, d, droptype, mobpos, chr, mob); + + // Quest Drops + d = dropItemsFromMonsterOnMap(visibleQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob); + dropItemsFromMonsterOnMap(otherQuestEntry, pos, d, chRate, droptype, mobpos, chr, mob); + } + } + private class ActivateItemReactor implements Runnable { private MapleMapItem mapitem; @@ -3292,18 +3437,11 @@ public class MapleMap { return closest; } - public void respawn() { - if(!allowSummons) return; - - chrRLock.lock(); - try { - if(characters.isEmpty()) { - return; - } - } finally { - chrRLock.unlock(); - } - + private static double getCurrentSpawnRate(int numPlayers) { + return 0.550 + (0.075 * Math.min(6, numPlayers)); + } + + private int getNumShouldSpawn(int numPlayers) { /* System.out.println("----------------------------------"); for (SpawnPoint spawnPoint : monsterSpawn) { @@ -3313,7 +3451,30 @@ public class MapleMap { System.out.println("----------------------------------"); */ - short numShouldSpawn = (short) ((monsterSpawn.size() - spawnedMonstersOnMap.get()));//Fking lol'd + if(ServerConstants.USE_ENABLE_FULL_RESPAWN) { + return (monsterSpawn.size() - spawnedMonstersOnMap.get()); + } + + int maxNumShouldSpawn = (int) Math.ceil(getCurrentSpawnRate(numPlayers) * monsterSpawn.size()); + return maxNumShouldSpawn - spawnedMonstersOnMap.get(); + } + + public void respawn() { + if(!allowSummons) return; + + int numPlayers; + chrRLock.lock(); + try { + numPlayers = characters.size(); + + if(numPlayers == 0) { + return; + } + } finally { + chrRLock.unlock(); + } + + int numShouldSpawn = getNumShouldSpawn(numPlayers); if(numShouldSpawn > 0) { List randomSpawn = new ArrayList<>(monsterSpawn); Collections.shuffle(randomSpawn); @@ -3745,14 +3906,6 @@ public class MapleMap { this.setDocked(state); } - public boolean isLastDoorOwner(int cid) { - return lastDoorOwner == cid; - } - - public void setLastDoorOwner(int cid) { - lastDoorOwner = cid; - } - public boolean isDojoMap() { return mapid >= 925020000 && mapid < 925040000; } @@ -3843,6 +3996,11 @@ public class MapleMap { expireItemsTask.cancel(false); expireItemsTask = null; } + + if(mobSpawnLootTask != null) { + mobSpawnLootTask.cancel(false); + mobSpawnLootTask = null; + } } finally { chrWLock.unlock(); } diff --git a/src/server/maps/MapleMapFactory.java b/src/server/maps/MapleMapFactory.java index 072e1e3779..9702f58cce 100644 --- a/src/server/maps/MapleMapFactory.java +++ b/src/server/maps/MapleMapFactory.java @@ -51,9 +51,9 @@ import scripting.event.EventInstanceManager; import tools.DatabaseConnection; import tools.StringUtil; - public class MapleMapFactory { - + private static Map mapRecoveryRate = new HashMap<>(); + private MapleDataProvider source; private MapleData nameData; private EventInstanceManager event; @@ -278,7 +278,13 @@ public class MapleMapFactory { map.setTimeLimit(MapleDataTool.getIntConvert("timeLimit", infoData, -1)); map.setFieldType(MapleDataTool.getIntConvert("fieldType", infoData, 0)); map.setMobCapacity(MapleDataTool.getIntConvert("fixedMobCapacity", infoData, 500));//Is there a map that contains more than 500 mobs? - + + MapleData recData = infoData.getChildByPath("recovery"); + if(recData != null) { + float recoveryRate = MapleDataTool.getFloat(recData); + mapRecoveryRate.put(mapid, recoveryRate); + } + HashMap backTypes = new HashMap<>(); try { for (MapleData layer : mapData.getChildByPath("back")) { // yolo @@ -443,4 +449,9 @@ public class MapleMapFactory { this.event = null; } + + public static float getMapRecoveryRate(int mapid) { + Float recRate = mapRecoveryRate.get(mapid); + return recRate != null ? recRate : 1.0f; + } } diff --git a/src/server/maps/MapleMapItem.java b/src/server/maps/MapleMapItem.java index 77a7da89bb..3d883a94d2 100644 --- a/src/server/maps/MapleMapItem.java +++ b/src/server/maps/MapleMapItem.java @@ -25,9 +25,9 @@ import client.MapleClient; import client.inventory.Item; import java.awt.Point; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; import tools.MaplePacketCreator; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; public class MapleMapItem extends AbstractMapleMapObject { protected MapleClient ownerClient; @@ -37,7 +37,7 @@ public class MapleMapItem extends AbstractMapleMapObject { protected byte type; protected boolean pickedUp = false, playerDrop; protected long dropTime; - private Lock itemLock = new MonitoredReentrantLock(MonitoredLockType.MAP_ITEM); + private Lock itemLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MAP_ITEM); public MapleMapItem(Item item, Point position, MapleMapObject dropper, MapleCharacter owner, MapleClient ownerClient, byte type, boolean playerDrop) { setPosition(position); diff --git a/src/server/maps/MapleMiniDungeon.java b/src/server/maps/MapleMiniDungeon.java index 487bf5a25a..ce2d386ffb 100644 --- a/src/server/maps/MapleMiniDungeon.java +++ b/src/server/maps/MapleMiniDungeon.java @@ -26,9 +26,9 @@ import java.util.List; import java.util.ArrayList; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; import tools.MaplePacketCreator; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -37,7 +37,7 @@ import net.server.audit.locks.MonitoredLockType; public class MapleMiniDungeon { List players = new ArrayList<>(); ScheduledFuture timeoutTask = null; - Lock lock = new MonitoredReentrantLock(MonitoredLockType.MINIDUNGEON, true); + Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.MINIDUNGEON, true); int baseMap; long expireTime; diff --git a/src/server/maps/MaplePlayerShop.java b/src/server/maps/MaplePlayerShop.java index 12ffd58a66..32c8544da7 100644 --- a/src/server/maps/MaplePlayerShop.java +++ b/src/server/maps/MaplePlayerShop.java @@ -28,7 +28,6 @@ import client.inventory.MapleInventory; import client.inventory.MapleInventoryType; import client.inventory.manipulator.MapleInventoryManipulator; import client.inventory.manipulator.MapleKarmaManipulator; -import constants.ServerConstants; import java.util.ArrayList; import java.util.Collections; import java.util.LinkedList; @@ -37,12 +36,12 @@ import java.util.LinkedHashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; import net.opcodes.SendOpcode; import tools.MaplePacketCreator; import tools.Pair; import tools.data.output.MaplePacketLittleEndianWriter; import net.server.audit.locks.MonitoredLockType; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; /** * @@ -62,7 +61,7 @@ public class MaplePlayerShop extends AbstractMapleMapObject { private List bannedList = new ArrayList<>(); private List> chatLog = new LinkedList<>(); private Map chatSlot = new LinkedHashMap<>(); - private Lock visitorLock = new MonitoredReentrantLock(MonitoredLockType.VISITOR_PSHOP, true); + private Lock visitorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.VISITOR_PSHOP, true); public MaplePlayerShop(MapleCharacter owner, String description, int itemid) { this.setPosition(owner.getPosition()); diff --git a/src/server/maps/MapleReactor.java b/src/server/maps/MapleReactor.java index f0d1aceff4..72df5fcfbc 100644 --- a/src/server/maps/MapleReactor.java +++ b/src/server/maps/MapleReactor.java @@ -29,7 +29,7 @@ import java.util.List; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.locks.Lock; -import net.server.audit.locks.MonitoredReentrantLock; +import net.server.audit.locks.factory.MonitoredReentrantLockFactory; import scripting.reactor.ReactorScriptManager; import server.TimerManager; @@ -54,8 +54,8 @@ public class MapleReactor extends AbstractMapleMapObject { private boolean shouldCollect; private boolean attackHit; private ScheduledFuture timeoutTask = null; - private Lock reactorLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR, true); - private Lock hitLock = new MonitoredReentrantLock(MonitoredLockType.REACTOR_HIT, true); + private Lock reactorLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.REACTOR, true); + private Lock hitLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.REACTOR_HIT, true); public MapleReactor(MapleReactorStats stats, int rid) { this.evstate = (byte)0; diff --git a/src/tools/MaplePacketCreator.java b/src/tools/MaplePacketCreator.java index 2fd5472694..da14024165 100644 --- a/src/tools/MaplePacketCreator.java +++ b/src/tools/MaplePacketCreator.java @@ -957,6 +957,7 @@ public class MaplePacketCreator { * Gets an update for specified stats. * * @param stats The stats to update. + * @param chr The update target. * @return The stat update packet. */ public static byte[] updatePlayerStats(List> stats, MapleCharacter chr) { @@ -968,6 +969,7 @@ public class MaplePacketCreator { * * @param stats The list of stats to update. * @param itemReaction Result of an item reaction(?) + * @param chr The update target. * @return The stat update packet. */ public static byte[] updatePlayerStats(List> stats, boolean itemReaction, MapleCharacter chr) { @@ -2189,7 +2191,7 @@ public class MaplePacketCreator { mplew.writeInt(hm.getObjectId()); mplew.writeMapleAsciiString(hm.getDescription()); mplew.write(hm.getItemId() % 100); - mplew.write(roomInfo); + mplew.write(roomInfo); // visitor capacity here, thanks GabrielSin! } public static byte[] updateHiredMerchantBox(MapleHiredMerchant hm) { @@ -4204,11 +4206,30 @@ public class MaplePacketCreator { mplew.writeShort(SendOpcode.FIELD_OBSTACLE_ONOFF.getValue()); mplew.writeMapleAsciiString(env); - mplew.writeInt(mode); + mplew.writeInt(mode); // 0: stop and back to start, 1: move return mplew.getPacket(); } - + + public static byte[] environmentMoveList(Set> envList) { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.FIELD_OBSTACLE_ONOFF_LIST.getValue()); + mplew.writeInt(envList.size()); + + for(Entry envMove : envList) { + mplew.writeMapleAsciiString(envMove.getKey()); + mplew.writeInt(envMove.getValue()); + } + + return mplew.getPacket(); + } + + public static byte[] environmentMoveReset() { + MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); + mplew.writeShort(SendOpcode.FIELD_OBSTACLE_ALL_RESET.getValue()); + return mplew.getPacket(); + } + public static byte[] startMapEffect(String msg, int itemid, boolean active) { final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter(); mplew.writeShort(SendOpcode.BLOW_WEATHER.getValue()); diff --git a/wz/Map.wz/Map/Map5/540000000.img.xml b/wz/Map.wz/Map/Map5/540000000.img.xml index ec03b09999..1d73c07c0c 100644 --- a/wz/Map.wz/Map/Map5/540000000.img.xml +++ b/wz/Map.wz/Map/Map5/540000000.img.xml @@ -1,9135 +1,21 @@ - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -9140,194 +26,9281 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + - - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Map.wz/Map/Map5/550000000.img.xml b/wz/Map.wz/Map/Map5/550000000.img.xml index 63134bace5..ddd1c5aac8 100644 --- a/wz/Map.wz/Map/Map5/550000000.img.xml +++ b/wz/Map.wz/Map/Map5/550000000.img.xml @@ -1,7916 +1,21 @@ - - - - - - - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -7921,183 +26,8054 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + - - - + + + - - - + + + - - - + + + - - - + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Map.wz/Map/Map5/550000400.img.xml b/wz/Map.wz/Map/Map5/550000400.img.xml index cc2ea26727..2939dd4307 100644 --- a/wz/Map.wz/Map/Map5/550000400.img.xml +++ b/wz/Map.wz/Map/Map5/550000400.img.xml @@ -1,2698 +1,3131 @@ - - - - - - - - + + + + + + + + - - + - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + - - + + - - + + - - + + - - - - - - - - + + - - - - - + - - - - - + - + + + + + - - - - - + - + + + + + - - - - - + - + + + + + - - - - - + - + + + + + - - - - - + - + + + + + - - - - - - - - - - - - - - - - - - - - - + - + + + + + - + + + + + - - - - - + - + + + + + - - - - - + - + + + + + - - - - - + - + + + + + - - - - - + - - - - - + - - - - + + + + - + + + + + - + + + + + - - - - - + - + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + - + - - + + + + + + + + + - - - - + + + + + + + + + + + - + + + + + + + + - + + - - - - - - - - - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + - - + - - - - - + + + - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - + + + - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - + + + - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + - - - - - - - - + + + + - + + + + + + + - + + - + - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - + - + + + + + + + + + + + + + + + + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - + + + - - + - + + + + + + + + + - - + - - - - - + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + - - + + - + - - - + + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + + + - - - - - + + + + + - - - - - + - - - - - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - + + + + + - - - - - - - - - - - - - - - - - - - - - + - - - - - + - + + + + + - + + + + + - - - - - + - + + + + + - - - - - + - + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - + @@ -2700,7 +3133,7 @@ - + @@ -2715,10 +3148,10 @@ - + - + @@ -2726,1219 +3159,355 @@ - + - - + + - + - - + + - - - - - - - - - - - - - - - - - - - - - - - - - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - + - - + + - - - - + + + + + + + + + + + + + + + + + + + + - - + + - + - - + + - - + + - + - - + + - - - - - - - - - - - - - - - - - + - - + + - - - - - - - - - + + + + + + + - - - - - - - + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + - + - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + - - - - - - + + + + + + - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + @@ -3946,17 +3515,65 @@ - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - + + + + + + + + + + + @@ -3964,96 +3581,485 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + - - - - - - + + + + + + + + + + - - - - - - + + + + + + + + + + - - - - - - + + + + + + + + + + - - - - - - + + + + + + + + + + - - - - - - + + + + + + + + + + - - - - - - + + + + + + + + + + - - - - - - - - - - - - - - - - - - + + + + + + + + + + + - - - - - - - - - - - + + + + + + + + + + + - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Map.wz/Map/Map5/551000000.img.xml b/wz/Map.wz/Map/Map5/551000000.img.xml index b34e12e1aa..fbf3514bcc 100644 --- a/wz/Map.wz/Map/Map5/551000000.img.xml +++ b/wz/Map.wz/Map/Map5/551000000.img.xml @@ -1,5451 +1,24 @@ - - - - - - - - + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -5456,165 +29,5585 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - - - - - + + + + + + - - - - - - - - - - + + + + + + - - - - - - - - - - + + + + + + - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/wz/Map.wz/Map/Map5/551000200.img.xml b/wz/Map.wz/Map/Map5/551000200.img.xml index 8f0a829bbe..1fb138a5c6 100644 --- a/wz/Map.wz/Map/Map5/551000200.img.xml +++ b/wz/Map.wz/Map/Map5/551000200.img.xml @@ -1,4883 +1,24 @@ - - - - - - - - + + + + + + + + - - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -4888,67 +29,4882 @@ - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - - - - - - + + + + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +