Event Recall + Cash Shop bestsellers + MapleSessionCoordinator rework

Implemented an event recall system. Players that went disconnected during an event instance are able to rejoin the ongoing event upon relogin.
Implemented a player-activity backed best-sellers system for the Cash Shop.
Patched the recently added selective loot system interfering with quest items, ever disabling drops after the player picked up one item.
Implemented a server flag for everlasting buffs.
Fixed some inconsistencies with Priest Dispel skill, sometimes crashing party players.
Fixed change job not properly showing effects for other players.
Fixed wrong fee value being taken from players that expands their guild size. Also, implemented GMS-like fee for this action.
Reworked the MapleSessionCoordinator, now evaluating client's HWID as well as remote IP. This's expected to lessen account drought time for players that are constantly changing their IP.

Last but not least, added world maps for Mushroom Castle, Zipangu, CBD/Malaysia and Ellin Forest regions. Original artwork content used on files depicted in this topic are rightful property of Nexon Corps., these files thoroughly trying to adhere the "Fair Use" disclaimer policy, their purpose being solely to fulfill gaming experience for the areas that were already present on v83 GMS but still lacked worldmaps. For more info regarding Fair Use, please refer to "http://www.dmlp.org/legal-guide/fair-use".
This commit is contained in:
ronancpl
2018-09-07 14:55:29 -03:00
parent 132f286391
commit c3e3c6dfbb
83 changed files with 2718 additions and 626 deletions
+237 -194
View File
@@ -998,7 +998,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public FameStatus canGiveFame(MapleCharacter from) {
if (gmLevel > 0) {
if (this.isGM()) {
return FameStatus.OK;
} else if (lastfametime >= System.currentTimeMillis() - 3600000 * 24) {
return FameStatus.NOT_TODAY;
@@ -1171,9 +1171,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
setMasteries(this.job.getId());
guildUpdate();
getMap().broadcastMessage(this, MaplePacketCreator.removePlayerFromMap(this.getId()), false);
getMap().broadcastMessage(this, MaplePacketCreator.spawnPlayerMapObject(this), false);
getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(getId(), 8), false);
getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(this.getId(), 8), false);
if (GameConstants.hasSPTable(newJob) && newJob.getId() != 2001) {
if (getBuffedValue(MapleBuffStat.MONSTER_RIDING) != null) {
@@ -1182,8 +1180,8 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
createDragon();
}
if(ServerConstants.USE_ANNOUNCE_CHANGEJOB) {
if(gmLevel > 1) {
if (ServerConstants.USE_ANNOUNCE_CHANGEJOB) {
if (!this.isGM()) {
broadcastAcquaintances(6, "[" + GameConstants.ordinal(GameConstants.getJobBranch(newJob)) + " Job] " + name + " has just become a " + newJob.name() + ".");
}
}
@@ -2793,7 +2791,6 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public enum FameStatus {
OK, NOT_TODAY, NOT_THIS_MONTH
}
@@ -2887,9 +2884,39 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
}
private synchronized int applyFame(int delta) {
int newFame = fame + delta;
if (newFame < -30000) {
delta = -(30000 + fame);
} else if (newFame > 30000) {
delta = 30000 - fame;
}
fame += delta;
return delta;
}
public void gainFame(int delta) {
this.addFame(delta);
this.updateSingleStat(MapleStat.FAME, this.fame);
gainFame(delta, null, 0);
}
public boolean gainFame(int delta, MapleCharacter fromPlayer, int mode) {
delta = applyFame(delta);
if (delta != 0) {
int thisFame = fame;
updateSingleStat(MapleStat.FAME, thisFame);
if (fromPlayer != null) {
fromPlayer.announce(MaplePacketCreator.giveFameResponse(mode, getName(), thisFame));
announce(MaplePacketCreator.receiveFame(mode, fromPlayer.getName()));
} else {
announce(MaplePacketCreator.getShowFameGain(delta));
}
return true;
} else {
return false;
}
}
public void gainMeso(int gain) {
@@ -5417,16 +5444,18 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return getInventory(MapleInventoryType.getByType(invType)).getNextFreeSlot() > -1;
}
public void increaseGuildCapacity() { //hopefully nothing is null
if (getMeso() < MapleGuild.getIncreaseGuildCost(getGuild().getCapacity())) {
public void increaseGuildCapacity() {
int cost = MapleGuild.getIncreaseGuildCost(getGuild().getCapacity());
if (getMeso() < cost) {
dropMessage(1, "You don't have enough mesos.");
return;
}
if(Server.getInstance().increaseGuildCapacity(guildid)) {
gainMeso(-MapleGuild.getIncreaseGuildCost(getGuild().getCapacity()), true, false, false);
gainMeso(-cost, true, false, true);
} else {
dropMessage(1, "You guild already reached the maximum capacity of players.");
dropMessage(1, "Your guild already reached the maximum capacity of players.");
}
}
@@ -5581,155 +5610,160 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
public void levelUp(boolean takeexp) {
Skill improvingMaxHP = null;
Skill improvingMaxMP = null;
int improvingMaxHPLevel = 0;
int improvingMaxMPLevel = 0;
petLock.lock();
try {
Skill improvingMaxHP = null;
Skill improvingMaxMP = null;
int improvingMaxHPLevel = 0;
int improvingMaxMPLevel = 0;
boolean isBeginner = isBeginnerJob();
if (ServerConstants.USE_AUTOASSIGN_STARTERS_AP && isBeginner && level < 11) {
remainingAp = 0;
if (level < 6) {
str += 5;
} else {
str += 4;
dex += 1;
}
} else {
remainingAp += 5;
if (isCygnus() && level > 10 && level < 70) {
remainingAp++;
}
}
if (isBeginner) {
maxhp += Randomizer.rand(12, 16);
maxmp += Randomizer.rand(10, 12);
} else if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) {
improvingMaxHP = isCygnus() ? SkillFactory.getSkill(DawnWarrior.MAX_HP_INCREASE) : SkillFactory.getSkill(Swordsman.IMPROVED_MAX_HP_INCREASE);
if (job.isA(MapleJob.CRUSADER)) {
improvingMaxMP = SkillFactory.getSkill(1210000);
} else if (job.isA(MapleJob.DAWNWARRIOR2)) {
improvingMaxMP = SkillFactory.getSkill(11110000);
}
improvingMaxHPLevel = getSkillLevel(improvingMaxHP);
maxhp += Randomizer.rand(24, 28);
maxmp += Randomizer.rand(4, 6);
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
improvingMaxMP = isCygnus() ? SkillFactory.getSkill(BlazeWizard.INCREASING_MAX_MP) : SkillFactory.getSkill(Magician.IMPROVED_MAX_MP_INCREASE);
improvingMaxMPLevel = getSkillLevel(improvingMaxMP);
maxhp += Randomizer.rand(10, 14);
maxmp += Randomizer.rand(22, 24);
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.THIEF) || (job.getId() > 1299 && job.getId() < 1500)) {
maxhp += Randomizer.rand(20, 24);
maxmp += Randomizer.rand(14, 16);
} else if (job.isA(MapleJob.GM)) {
maxhp = 30000;
maxmp = 30000;
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
improvingMaxHP = isCygnus() ? SkillFactory.getSkill(ThunderBreaker.IMPROVE_MAX_HP) : SkillFactory.getSkill(5100000);
improvingMaxHPLevel = getSkillLevel(improvingMaxHP);
maxhp += Randomizer.rand(22, 28);
maxmp += Randomizer.rand(18, 23);
} else if (job.isA(MapleJob.ARAN1)) {
maxhp += Randomizer.rand(44, 48);
int aids = Randomizer.rand(4, 8);
maxmp += aids + Math.floor(aids * 0.1);
}
if (improvingMaxHPLevel > 0 && (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.PIRATE) || job.isA(MapleJob.DAWNWARRIOR1))) {
maxhp += improvingMaxHP.getEffect(improvingMaxHPLevel).getX();
}
if (improvingMaxMPLevel > 0 && (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.CRUSADER) || job.isA(MapleJob.BLAZEWIZARD1))) {
maxmp += improvingMaxMP.getEffect(improvingMaxMPLevel).getX();
}
if (ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
maxmp += localint_ / 20;
} else {
maxmp += localint_ / 10;
}
}
if (takeexp) {
exp.addAndGet(-ExpTable.getExpNeededForLevel(level));
if (exp.get() < 0) {
exp.set(0);
}
}
level++;
if (level >= getMaxClassLevel()) {
exp.set(0);
level = getMaxClassLevel(); //To prevent levels past the maximum
}
maxhp = Math.min(30000, maxhp);
maxmp = Math.min(30000, maxmp);
if (level == 200) {
exp.set(0);
if(ServerConstants.PLAYERNPC_AUTODEPLOY && !this.isGM()) MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), this);
}
recalcLocalStats();
hp = localmaxhp;
mp = localmaxmp;
List<Pair<MapleStat, Integer>> statup = new ArrayList<>(10);
statup.add(new Pair<>(MapleStat.AVAILABLEAP, remainingAp));
statup.add(new Pair<>(MapleStat.HP, localmaxhp));
statup.add(new Pair<>(MapleStat.MP, localmaxmp));
statup.add(new Pair<>(MapleStat.EXP, exp.get()));
statup.add(new Pair<>(MapleStat.LEVEL, level));
statup.add(new Pair<>(MapleStat.MAXHP, maxhp));
statup.add(new Pair<>(MapleStat.MAXMP, maxmp));
statup.add(new Pair<>(MapleStat.STR, Math.min(str, Short.MAX_VALUE)));
statup.add(new Pair<>(MapleStat.DEX, Math.min(dex, Short.MAX_VALUE)));
if (job.getId() % 1000 > 0) {
remainingSp[GameConstants.getSkillBook(job.getId())] += 3;
statup.add(new Pair<>(MapleStat.AVAILABLESP, remainingSp[GameConstants.getSkillBook(job.getId())]));
}
client.announce(MaplePacketCreator.updatePlayerStats(statup, this));
getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(getId(), 0), false);
recalcLocalStats();
setMPC(new MaplePartyCharacter(this));
silentPartyUpdate();
if(level == 10 && party != null) {
if(this.isPartyLeader()) party.assignNewLeader(client);
PartyOperationHandler.leaveParty(party, mpc, client);
showHint("You have reached #blevel 10#k, therefore you must leave your #rstarter party#k.");
}
if (this.guildid > 0) {
getGuild().broadcast(MaplePacketCreator.levelUpMessage(2, level, name), this.getId());
}
if (ServerConstants.USE_PERFECT_PITCH && level >= 30) {
//milestones?
if (MapleInventoryManipulator.checkSpace(client, 4310000, (short) 1, "")) {
MapleInventoryManipulator.addById(client, 4310000, (short) 1);
}
}
if (level == 200 && !isGM()) {
final String names = (getMedalText() + name);
client.getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, String.format(LEVEL_200, names, names)));
}
if(level % 20 == 0 && ServerConstants.USE_ADD_SLOTS_BY_LEVEL == true) {
if (!isGM()) {
for (byte i = 1; i < 5; i++) {
gainSlots(i, 4, true);
boolean isBeginner = isBeginnerJob();
if (ServerConstants.USE_AUTOASSIGN_STARTERS_AP && isBeginner && level < 11) {
remainingAp = 0;
if (level < 6) {
str += 5;
} else {
str += 4;
dex += 1;
}
this.yellowMessage("You reached level " + level + ". Congratulations! As a token of your success, your inventory has been expanded a little bit.");
}
} else {
remainingAp += 5;
if (isCygnus() && level > 10 && level < 70) {
remainingAp++;
}
}
if (isBeginner) {
maxhp += Randomizer.rand(12, 16);
maxmp += Randomizer.rand(10, 12);
} else if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1)) {
improvingMaxHP = isCygnus() ? SkillFactory.getSkill(DawnWarrior.MAX_HP_INCREASE) : SkillFactory.getSkill(Swordsman.IMPROVED_MAX_HP_INCREASE);
if (job.isA(MapleJob.CRUSADER)) {
improvingMaxMP = SkillFactory.getSkill(1210000);
} else if (job.isA(MapleJob.DAWNWARRIOR2)) {
improvingMaxMP = SkillFactory.getSkill(11110000);
}
improvingMaxHPLevel = getSkillLevel(improvingMaxHP);
maxhp += Randomizer.rand(24, 28);
maxmp += Randomizer.rand(4, 6);
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
improvingMaxMP = isCygnus() ? SkillFactory.getSkill(BlazeWizard.INCREASING_MAX_MP) : SkillFactory.getSkill(Magician.IMPROVED_MAX_MP_INCREASE);
improvingMaxMPLevel = getSkillLevel(improvingMaxMP);
maxhp += Randomizer.rand(10, 14);
maxmp += Randomizer.rand(22, 24);
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.THIEF) || (job.getId() > 1299 && job.getId() < 1500)) {
maxhp += Randomizer.rand(20, 24);
maxmp += Randomizer.rand(14, 16);
} else if (job.isA(MapleJob.GM)) {
maxhp = 30000;
maxmp = 30000;
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
improvingMaxHP = isCygnus() ? SkillFactory.getSkill(ThunderBreaker.IMPROVE_MAX_HP) : SkillFactory.getSkill(5100000);
improvingMaxHPLevel = getSkillLevel(improvingMaxHP);
maxhp += Randomizer.rand(22, 28);
maxmp += Randomizer.rand(18, 23);
} else if (job.isA(MapleJob.ARAN1)) {
maxhp += Randomizer.rand(44, 48);
int aids = Randomizer.rand(4, 8);
maxmp += aids + Math.floor(aids * 0.1);
}
if (improvingMaxHPLevel > 0 && (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.PIRATE) || job.isA(MapleJob.DAWNWARRIOR1))) {
maxhp += improvingMaxHP.getEffect(improvingMaxHPLevel).getX();
}
if (improvingMaxMPLevel > 0 && (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.CRUSADER) || job.isA(MapleJob.BLAZEWIZARD1))) {
maxmp += improvingMaxMP.getEffect(improvingMaxMPLevel).getX();
}
if (ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
maxmp += localint_ / 20;
} else {
maxmp += localint_ / 10;
}
}
if (takeexp) {
exp.addAndGet(-ExpTable.getExpNeededForLevel(level));
if (exp.get() < 0) {
exp.set(0);
}
}
level++;
if (level >= getMaxClassLevel()) {
exp.set(0);
level = getMaxClassLevel(); //To prevent levels past the maximum
}
maxhp = Math.min(30000, maxhp);
maxmp = Math.min(30000, maxmp);
if (level == 200) {
exp.set(0);
if(ServerConstants.PLAYERNPC_AUTODEPLOY && !this.isGM()) MaplePlayerNPC.spawnPlayerNPC(GameConstants.getHallOfFameMapid(job), this);
}
recalcLocalStats();
hp = localmaxhp;
mp = localmaxmp;
List<Pair<MapleStat, Integer>> statup = new ArrayList<>(10);
statup.add(new Pair<>(MapleStat.AVAILABLEAP, remainingAp));
statup.add(new Pair<>(MapleStat.HP, localmaxhp));
statup.add(new Pair<>(MapleStat.MP, localmaxmp));
statup.add(new Pair<>(MapleStat.EXP, exp.get()));
statup.add(new Pair<>(MapleStat.LEVEL, level));
statup.add(new Pair<>(MapleStat.MAXHP, maxhp));
statup.add(new Pair<>(MapleStat.MAXMP, maxmp));
statup.add(new Pair<>(MapleStat.STR, Math.min(str, Short.MAX_VALUE)));
statup.add(new Pair<>(MapleStat.DEX, Math.min(dex, Short.MAX_VALUE)));
if (job.getId() % 1000 > 0) {
remainingSp[GameConstants.getSkillBook(job.getId())] += 3;
statup.add(new Pair<>(MapleStat.AVAILABLESP, remainingSp[GameConstants.getSkillBook(job.getId())]));
}
client.announce(MaplePacketCreator.updatePlayerStats(statup, this));
getMap().broadcastMessage(this, MaplePacketCreator.showForeignEffect(getId(), 0), false);
recalcLocalStats();
setMPC(new MaplePartyCharacter(this));
silentPartyUpdate();
if(level == 10 && party != null) {
if(this.isPartyLeader()) party.assignNewLeader(client);
PartyOperationHandler.leaveParty(party, mpc, client);
showHint("You have reached #blevel 10#k, therefore you must leave your #rstarter party#k.");
}
if (this.guildid > 0) {
getGuild().broadcast(MaplePacketCreator.levelUpMessage(2, level, name), this.getId());
}
if (ServerConstants.USE_PERFECT_PITCH && level >= 30) {
//milestones?
if (MapleInventoryManipulator.checkSpace(client, 4310000, (short) 1, "")) {
MapleInventoryManipulator.addById(client, 4310000, (short) 1);
}
}
if (level == 200 && !isGM()) {
final String names = (getMedalText() + name);
client.getWorldServer().broadcastPacket(MaplePacketCreator.serverNotice(6, String.format(LEVEL_200, names, names)));
}
if(level % 20 == 0 && ServerConstants.USE_ADD_SLOTS_BY_LEVEL == true) {
if (!isGM()) {
for (byte i = 1; i < 5; i++) {
gainSlots(i, 4, true);
}
this.yellowMessage("You reached level " + level + ". Congratulations! As a token of your success, your inventory has been expanded a little bit.");
}
}
if (level % 20 == 0 && ServerConstants.USE_ADD_RATES_BY_LEVEL == true) { //For the rate upgrade
revertLastPlayerRates();
setPlayerRates();
this.yellowMessage("You managed to get level " + level + "! Getting experience and items seems a little easier now, huh?");
}
levelUpMessages();
guildUpdate();
} finally {
petLock.unlock();
}
if (level % 20 == 0 && ServerConstants.USE_ADD_RATES_BY_LEVEL == true) { //For the rate upgrade
revertLastPlayerRates();
setPlayerRates();
this.yellowMessage("You managed to get level " + level + "! Getting experience and items seems a little easier now, huh?");
}
levelUpMessages();
guildUpdate();
}
public void gainAp(int amount) {
@@ -6939,27 +6973,25 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
visibleMapObjects.remove(mo);
}
public void resetStats() {
public synchronized void resetStats() {
if(!ServerConstants.USE_AUTOASSIGN_STARTERS_AP) {
return;
}
List<Pair<MapleStat, Integer>> statup = new ArrayList<>(5);
int tap = 0, tsp = 1;
int tap = remainingAp + str + dex + int_ + luk, tsp = 1;
int tstr = 4, tdex = 4, tint = 4, tluk = 4;
int levelap = (isCygnus() ? 6 : 5);
switch (job.getId()) {
case 100:
case 1100:
case 2100:
tstr = 35;
tap = ((getLevel() - 10) * levelap) + 14;
tsp += ((getLevel() - 10) * 3);
break;
case 200:
case 1200:
tint = 20;
tap = ((getLevel() - 8) * levelap) + 29;
tsp += ((getLevel() - 8) * 3);
break;
case 300:
@@ -6967,29 +6999,37 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
case 400:
case 1400:
tdex = 25;
tap = ((getLevel() - 10) * levelap) + 24;
tsp += ((getLevel() - 10) * 3);
break;
case 500:
case 1500:
tdex = 20;
tap = ((getLevel() - 10) * levelap) + 29;
tsp += ((getLevel() - 10) * 3);
break;
}
this.remainingAp = tap;
this.remainingSp[GameConstants.getSkillBook(job.getId())] = tsp;
this.dex = tdex;
this.int_ = tint;
this.str = tstr;
this.luk = tluk;
statup.add(new Pair<>(MapleStat.AVAILABLEAP, tap));
statup.add(new Pair<>(MapleStat.AVAILABLESP, tsp));
statup.add(new Pair<>(MapleStat.STR, tstr));
statup.add(new Pair<>(MapleStat.DEX, tdex));
statup.add(new Pair<>(MapleStat.INT, tint));
statup.add(new Pair<>(MapleStat.LUK, tluk));
announce(MaplePacketCreator.updatePlayerStats(statup, this));
tap -= tstr;
tap -= tdex;
tap -= tint;
tap -= tluk;
if (tap >= 0) {
this.remainingAp = tap;
this.remainingSp[GameConstants.getSkillBook(job.getId())] = tsp;
this.str = tstr;
this.dex = tdex;
this.int_ = tint;
this.luk = tluk;
statup.add(new Pair<>(MapleStat.AVAILABLEAP, tap));
statup.add(new Pair<>(MapleStat.AVAILABLESP, tsp));
statup.add(new Pair<>(MapleStat.STR, tstr));
statup.add(new Pair<>(MapleStat.DEX, tdex));
statup.add(new Pair<>(MapleStat.INT, tint));
statup.add(new Pair<>(MapleStat.LUK, tluk));
announce(MaplePacketCreator.updatePlayerStats(statup, this));
} else {
FilePrinter.print(FilePrinter.EXCEPTION_CAUGHT, name + " tried to get their stats reseted, without having enough AP available.");
}
}
public void resetBattleshipHp() {
@@ -8474,18 +8514,20 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}
announce(MaplePacketCreator.updateQuestInfo((short) qs.getQuest().getId(), qs.getNpc()));
}
private void fameGainByQuest() {
public void awardQuestPoint(int awardedPoints) {
if (ServerConstants.QUEST_POINT_REQUIREMENT < 1 || awardedPoints < 1) return;
int delta;
synchronized (quests) {
quest_fame += 1;
quest_fame += awardedPoints;
int delta = quest_fame / ServerConstants.FAME_GAIN_BY_QUEST;
if(delta > 0) {
gainFame(delta);
client.announce(MaplePacketCreator.getShowFameGain(delta));
}
quest_fame %= ServerConstants.FAME_GAIN_BY_QUEST;
delta = quest_fame / ServerConstants.QUEST_POINT_REQUIREMENT;
quest_fame %= ServerConstants.QUEST_POINT_REQUIREMENT;
}
if(delta > 0) {
gainFame(delta);
}
}
@@ -8503,8 +8545,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
MapleQuest mquest = quest.getQuest();
short questid = mquest.getId();
if(!mquest.isSameDayRepeatable() && !MapleQuest.isExploitableQuest(questid)) {
if(ServerConstants.FAME_GAIN_BY_QUEST > 0)
fameGainByQuest();
awardQuestPoint(ServerConstants.QUEST_POINT_PER_QUEST_COMPLETE);
}
announce(MaplePacketCreator.completeQuest(questid, quest.getCompletionTime()));
@@ -8572,7 +8613,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
private void runQuestExpireTask() {
petLock.lock();
try {
long timeNow = System.currentTimeMillis();
long timeNow = Server.getInstance().getCurrentTime();
List<MapleQuest> expireList = new LinkedList<>();
for(Entry<MapleQuest, Long> qe : questExpirations.entrySet()) {
@@ -8607,7 +8648,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
}, 10 * 1000);
}
questExpirations.put(quest, System.currentTimeMillis() + time);
questExpirations.put(quest, Server.getInstance().getCurrentTime() + time);
} finally {
petLock.unlock();
}
@@ -8880,6 +8921,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
return false;
}
//EVENTS
private byte team = 0;
private MapleFitness fitness;
@@ -8917,6 +8959,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
public void setLastSnowballAttack(long time) {
this.snowballattack = time;
}
//Monster Carnival
private int cp = 0;
private int obtainedcp = 0;
@@ -9059,7 +9102,7 @@ public class MapleCharacter extends AbstractAnimatedMapleMapObject {
continue;
}
if ((nEquip.getItemLevel() < ServerConstants.USE_EQUIPMNT_LVLUP) || (itemName.contains("Reverse") && nEquip.getItemLevel() < 4) || (itemName.contains("Timeless") && nEquip.getItemLevel() < 6)) {
if ((nEquip.getItemLevel() < ServerConstants.USE_EQUIPMNT_LVLUP) || (nEquip.getItemLevel() < ii.getEquipLevel(nEquip.getItemId(), true))) {
nEquip.gainItemExp(client, expGain);
}
}
+44 -54
View File
@@ -85,6 +85,8 @@ public class MapleClient {
public static final int LOGIN_SERVER_TRANSITION = 1;
public static final int LOGIN_LOGGEDIN = 2;
public static final String CLIENT_KEY = "CLIENT";
public static final String CLIENT_HWID = "HWID";
public static final String CLIENT_NIBBLEHWID = "HWID2";
private MapleAESOFB send;
private MapleAESOFB receive;
private final IoSession session;
@@ -507,7 +509,7 @@ public class MapleClient {
return false;
}
public int login(String login, String pwd) {
public int login(String login, String pwd, String nibbleHwid) {
loginattempt++;
if (loginattempt > 4) {
MapleSessionCoordinator.getInstance().closeSession(session, false);
@@ -571,7 +573,7 @@ public class MapleClient {
}
if (loginok == 0 || loginok == 4) {
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptSessionLogin(session, accId, loginok == 4);
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptLoginSession(session, nibbleHwid, accId, loginok == 4);
switch (res) {
case SUCCESS:
@@ -887,71 +889,59 @@ public class MapleClient {
final World wserv = getWorldServer(); // obviously wserv is NOT null if this player was online on it
try {
if (channel == -1 || shutdown) {
if(chrg != null) chrg.setCharacter(null);
wserv.removePlayer(player);
removePlayer(wserv);
player.saveCooldowns();
player.cancelAllDebuffs();
player.saveCharToDB(true);
clear();
return;
}
removePlayer(wserv);
if (!cashshop) {
if (!this.serverTransition) { // meaning not changing channels
if (messengerid > 0) {
wserv.leaveMessenger(messengerid, chrm);
}
/*
if (fid > 0) {
final MapleFamily family = worlda.getFamily(fid);
family.
if (!(channel == -1 || shutdown)) {
if (!cashshop) {
if (!this.serverTransition) { // meaning not changing channels
if (messengerid > 0) {
wserv.leaveMessenger(messengerid, chrm);
}
/*
if (fid > 0) {
final MapleFamily family = worlda.getFamily(fid);
family.
}
*/
for (MapleQuestStatus status : player.getStartedQuests()) { //This is for those quests that you have to stay logged in for a certain amount of time
MapleQuest quest = status.getQuest();
if (quest.getTimeLimit() > 0) {
MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
newStatus.setForfeited(player.getQuest(quest).getForfeited() + 1);
player.updateQuest(newStatus);
}
}
if (guild != null) {
final Server server = Server.getInstance();
server.setGuildMemberOnline(player, false, player.getClient().getChannel());
player.getClient().announce(MaplePacketCreator.showGuildInfo(player));
}
if (bl != null) {
wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
}
}
*/
for (MapleQuestStatus status : player.getStartedQuests()) { //This is for those quests that you have to stay logged in for a certain amount of time
MapleQuest quest = status.getQuest();
if (quest.getTimeLimit() > 0) {
MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
newStatus.setForfeited(player.getQuest(quest).getForfeited() + 1);
player.updateQuest(newStatus);
}
}
if (guild != null) {
final Server server = Server.getInstance();
server.setGuildMemberOnline(player, false, player.getClient().getChannel());
player.getClient().announce(MaplePacketCreator.showGuildInfo(player));
}
if (bl != null) {
wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
}
}
} else {
if (!this.serverTransition) { // if dc inside of cash shop.
if (bl != null) {
wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
}
}
}
} else {
if (!this.serverTransition) { // if dc inside of cash shop.
if (bl != null) {
wserv.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
}
}
}
}
} catch (final Exception e) {
FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e);
} finally {
if (!this.serverTransition) {
if(chrg != null) chrg.setCharacter(null);
wserv.removePlayer(player);
//getChannelServer().removePlayer(player); already being done
player.saveCooldowns();
player.cancelAllDebuffs();
player.saveCharToDB(true);
if (player != null) {//no idea, occur :(
player.empty(false);
}
player.logOff();
clear();
} else {
getChannelServer().removePlayer(player);
+1 -1
View File
@@ -301,7 +301,7 @@ public class CommandsExecutor {
addCommand("servermessage", 4, ServerMessageCommand.class);
addCommand("proitem", 4, ProItemCommand.class);
addCommand("seteqstat", 4, SetQStatCommand.class);
addCommand("seteqstat", 4, SetEqStatCommand.class);
addCommand("exprate", 4, ExpRateCommand.class);
addCommand("mesorate", 4, MesoRateCommand.class);
addCommand("droprate", 4, DropRateCommand.class);
@@ -26,6 +26,7 @@ package client.command.commands.v1;
import client.MapleCharacter;
import client.command.Command;
import client.MapleClient;
import constants.GameConstants;
import server.MaplePortal;
import server.maps.MapleMap;
@@ -38,56 +39,7 @@ public class GotoCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
final HashMap<String, Integer> gotomaps = new HashMap<String, Integer>();
gotomaps.put("gmmap", 180000000);
gotomaps.put("southperry", 60000);
gotomaps.put("amherst", 1000000);
gotomaps.put("henesys", 100000000);
gotomaps.put("ellinia", 101000000);
gotomaps.put("perion", 102000000);
gotomaps.put("kerning", 103000000);
gotomaps.put("lith", 104000000);
gotomaps.put("sleepywood", 105040300);
gotomaps.put("florina", 110000000);
gotomaps.put("nautilus", 120000000);
gotomaps.put("ereve", 130000000);
gotomaps.put("rien", 140000000);
gotomaps.put("orbis", 200000000);
gotomaps.put("happy", 209000000);
gotomaps.put("elnath", 211000000);
gotomaps.put("ludi", 220000000);
gotomaps.put("aqua", 230000000);
gotomaps.put("leafre", 240000000);
gotomaps.put("mulung", 250000000);
gotomaps.put("herb", 251000000);
gotomaps.put("omega", 221000000);
gotomaps.put("korean", 222000000);
gotomaps.put("ellin", 300000000);
gotomaps.put("nlc", 600000000);
gotomaps.put("excavation", 990000000);
gotomaps.put("pianus", 230040420);
gotomaps.put("horntail", 240060200);
gotomaps.put("mushmom", 100000005);
gotomaps.put("griffey", 240020101);
gotomaps.put("manon", 240020401);
gotomaps.put("horseman", 682000001);
gotomaps.put("balrog", 105090900);
gotomaps.put("zakum", 211042300);
gotomaps.put("papu", 220080001);
gotomaps.put("showa", 801000000);
gotomaps.put("guild", 200000301);
gotomaps.put("shrine", 800000000);
gotomaps.put("skelegon", 240040511);
gotomaps.put("hpq", 100000200);
gotomaps.put("ht", 240050400);
gotomaps.put("ariant", 260000000);
gotomaps.put("magatia", 261000000);
gotomaps.put("singapore", 540000000);
gotomaps.put("keep", 610020006);
gotomaps.put("amoria", 680000000);
gotomaps.put("temple", 270000100);
gotomaps.put("neo", 240070000);
gotomaps.put("fm", 910000000);
final HashMap<String, Integer> gotomaps = GameConstants.GOTO_MAPS;
MapleCharacter player = c.getPlayer();
if (params.length < 1){
@@ -42,11 +42,12 @@ public class ProItemCommand extends Command {
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
if (params.length < 2) {
player.yellowMessage("Syntax: !proitem <itemid> <statvalue>");
player.yellowMessage("Syntax: !proitem <itemid> <stat value> [<spdjmp value>]");
return;
}
int itemid = Integer.parseInt(params[0]);
short multiply = Short.parseShort(params[1]);
short stat = (short) Math.max(0, Short.parseShort(params[1]));
short spdjmp = params.length >= 3 ? (short) Math.max(0, Short.parseShort(params[2])) : 0;
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleInventoryType type = ItemConstants.getInventoryType(itemid);
@@ -54,13 +55,13 @@ public class ProItemCommand extends Command {
Item it = ii.getEquipById(itemid);
it.setOwner(player.getName());
hardsetItemStats((Equip) it, multiply);
hardsetItemStats((Equip) it, stat, spdjmp);
MapleInventoryManipulator.addFromDrop(c, it);
} else {
player.dropMessage(6, "Make sure it's an equippable item.");
}
}
private static void hardsetItemStats(Equip equip, short stat) {
private static void hardsetItemStats(Equip equip, short stat, short spdjmp) {
equip.setStr(stat);
equip.setDex(stat);
equip.setInt(stat);
@@ -69,8 +70,8 @@ public class ProItemCommand extends Command {
equip.setWatk(stat);
equip.setAcc(stat);
equip.setAvoid(stat);
equip.setJump(stat);
equip.setSpeed(stat);
equip.setJump(spdjmp);
equip.setSpeed(spdjmp);
equip.setWdef(stat);
equip.setMdef(stat);
equip.setHp(stat);
@@ -31,7 +31,7 @@ import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import constants.ItemConstants;
public class SetQStatCommand extends Command {
public class SetEqStatCommand extends Command {
{
setDescription("");
}
@@ -40,33 +40,33 @@ public class SetQStatCommand extends Command {
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !seteqstat <statvalue>");
player.yellowMessage("Syntax: !seteqstat <stat value> [<spdjmp value>]");
return;
}
int newStat = Integer.parseInt(params[0]);
short newStat = (short) Math.max(0, Integer.parseInt(params[0]));
short newSpdJmp = params.length >= 2 ? (short) Integer.parseInt(params[1]) : 0;
MapleInventory equip = player.getInventory(MapleInventoryType.EQUIP);
for (byte i = 1; i <= equip.getSlotLimit(); i++) {
try {
Equip eu = (Equip) equip.getItem(i);
if (eu == null) continue;
short incval = (short) newStat;
eu.setWdef(incval);
eu.setAcc(incval);
eu.setAvoid(incval);
eu.setJump(incval);
eu.setMatk(incval);
eu.setMdef(incval);
eu.setHp(incval);
eu.setMp(incval);
eu.setSpeed(incval);
eu.setWatk(incval);
eu.setDex(incval);
eu.setInt(incval);
eu.setStr(incval);
eu.setLuk(incval);
eu.setWdef(newStat);
eu.setAcc(newStat);
eu.setAvoid(newStat);
eu.setJump(newSpdJmp);
eu.setMatk(newStat);
eu.setMdef(newStat);
eu.setHp(newStat);
eu.setMp(newStat);
eu.setSpeed(newSpdJmp);
eu.setWatk(newStat);
eu.setDex(newStat);
eu.setInt(newStat);
eu.setStr(newStat);
eu.setLuk(newStat);
byte flag = eu.getFlag();
flag |= ItemConstants.UNTRADEABLE;
+8 -12
View File
@@ -66,7 +66,7 @@ public class Equip extends Item {
private float itemExp;
private int ringid = -1;
private boolean wear = false;
private boolean isUpgradeable, isElemental = false; // timeless or reverse
private boolean isUpgradeable, isElemental = false; // timeless or reverse, or any equip that could levelup on GMS for all effects
public Equip(int id, short position) {
this(id, position, 0);
@@ -78,8 +78,7 @@ public class Equip extends Item {
this.itemExp = 0;
this.itemLevel = 1;
String itemName = MapleItemInformationProvider.getInstance().getName(id);
if(itemName != null) this.isElemental = (itemName.contains("Timeless") || itemName.contains("Reverse"));
this.isElemental = (MapleItemInformationProvider.getInstance().getEquipLevel(id, false) > 1);
}
@Override
@@ -533,17 +532,14 @@ public class Equip extends Item {
}
}
private boolean reachedMaxLevel(String eqpName) {
if(isElemental) {
if(eqpName.contains("Timeless")) {
if(itemLevel < 6) return false;
} else {
if(itemLevel < 4) return false;
private boolean reachedMaxLevel() {
if (isElemental) {
if (itemLevel < MapleItemInformationProvider.getInstance().getEquipLevel(getItemId(), true)) {
return false;
}
}
if(itemLevel < ServerConstants.USE_EQUIPMNT_LVLUP) return false;
return true;
return itemLevel >= ServerConstants.USE_EQUIPMNT_LVLUP;
}
public String showEquipFeatures(MapleClient c) {
@@ -551,7 +547,7 @@ public class Equip extends Item {
if(!ii.isUpgradeable(this.getItemId())) return "";
String eqpName = ii.getName(getItemId());
String eqpInfo = reachedMaxLevel(eqpName) ? " #e#rMAX LEVEL#k#n" : (" EXP: #e#b" + (int)itemExp + "#k#n / " + ExpTable.getEquipExpNeededForLevel(itemLevel));
String eqpInfo = reachedMaxLevel() ? " #e#rMAX LEVEL#k#n" : (" EXP: #e#b" + (int)itemExp + "#k#n / " + ExpTable.getEquipExpNeededForLevel(itemLevel));
return "'" + eqpName + "' -> LV: #e#b" + itemLevel + "#k#n " + eqpInfo + "\r\n";
}
+4 -2
View File
@@ -25,10 +25,12 @@ import constants.ItemConstants;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
public class Item implements Comparable<Item> {
private static AtomicInteger runningCashId = new AtomicInteger(0);
private int id, cashId, sn;
private short position;
private short quantity;
@@ -81,7 +83,7 @@ public class Item implements Comparable<Item> {
public int getCashId() {
if (cashId == 0) {
cashId = new Random().nextInt(Integer.MAX_VALUE) + 1;
cashId = runningCashId.incrementAndGet();
}
return cashId;
}
+58 -1
View File
@@ -17,7 +17,8 @@ import server.quest.MapleQuest;
*/
public class GameConstants {
public static String[] WORLD_NAMES = {"Scania", "Bera", "Broa", "Windia", "Khaini", "Bellocan", "Mardia", "Kradia", "Yellonde", "Demethos", "Galicia", "El Nido", "Zenith", "Arcenia", "Kastia", "Judis", "Plana", "Kalluna", "Stius", "Croa", "Medere"};
public static final int[] OWL_DATA = new int[]{1082002, 2070005, 2070006, 1022047, 1102041, 2044705, 2340000, 2040017, 1092030, 2040804};
public static final int[] OWL_DATA = new int[]{1082002, 2070005, 2070006, 1022047, 1102041, 2044705, 2340000, 2040017, 1092030, 2040804};
public static final int[] CASH_DATA = new int[]{50200004, 50200069, 50200117, 50100008, 50000047};
// Ronan's rates upgrade system
private static final int[] DROP_RATE_GAIN = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14};
@@ -40,6 +41,62 @@ public class GameConstants {
return(EXP_RATE_GAIN[slot]);
}
// used by the "goto" command
public static final HashMap<String, Integer> GOTO_MAPS = new HashMap<String, Integer>() {{
put("gmmap", 180000000);
put("southperry", 60000);
put("amherst", 1000000);
put("henesys", 100000000);
put("ellinia", 101000000);
put("perion", 102000000);
put("kerning", 103000000);
put("lith", 104000000);
put("sleepywood", 105040300);
put("florina", 110000000);
put("nautilus", 120000000);
put("ereve", 130000000);
put("rien", 140000000);
put("orbis", 200000000);
put("happy", 209000000);
put("elnath", 211000000);
put("ludi", 220000000);
put("aqua", 230000000);
put("leafre", 240000000);
put("mulung", 250000000);
put("herb", 251000000);
put("omega", 221000000);
put("korean", 222000000);
put("ellin", 300000000);
put("nlc", 600000000);
put("excavation", 990000000);
put("pianus", 230040420);
put("horntail", 240060200);
put("mushmom", 100000005);
put("griffey", 240020101);
put("manon", 240020401);
put("horseman", 682000001);
put("balrog", 105090900);
put("zakum", 211042300);
put("papu", 220080001);
put("showa", 801000000);
put("guild", 200000301);
put("shrine", 800000000);
put("skelegon", 240040511);
put("hpq", 100000200);
put("ht", 240050400);
put("ariant", 260000000);
put("magatia", 261000000);
put("singapore", 540000000);
put("quay", 541000000);
put("kampung", 551000000);
put("keep", 610020006);
put("amoria", 680000000);
put("temple", 270000100);
put("square", 103040000);
put("neo", 240070000);
put("fm", 910000000);
}};
// MapleStory default keyset
private static final int[] DEFAULT_KEY = {18, 65, 2, 23, 3, 4, 5, 6, 16, 17, 19, 25, 26, 27, 31, 34, 35, 37, 38, 40, 43, 44, 45, 46, 50, 56, 59, 60, 61, 62, 63, 64, 57, 48, 29, 7, 24, 33, 41, 39};
private static final int[] DEFAULT_TYPE = {4, 6, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 5, 5, 4, 4, 5, 6, 6, 6, 6, 6, 6, 5, 4, 5, 4, 4, 4, 4, 4};
+17 -6
View File
@@ -33,12 +33,12 @@ public class ServerConstants {
public static final boolean AUTOMATIC_REGISTER = true; //Automatically register players when they login with a nonexistent username.
public static final boolean BCRYPT_MIGRATION = true; //Performs a migration from old SHA-1 and SHA-512 password to bcrypt.
public static final boolean COLLECTIVE_CHARSLOT = false; //Available character slots are contabilized globally rather than per world server.
public static final boolean DETERRED_MULTICLIENT = false; //Enables multi-client and suspicious remote IP detection on the login system.
public static final boolean DETERRED_MULTICLIENT = false; //Enables multi-client and suspicious remote IP detection on the login system.
//Besides blocking logging in with several client sessions on the same machine, this also blocks suspicious login attempts for players that tries to login on an account using several diferent remote addresses.
//Multiclient Coordinator Configuration
public static final int MAX_ALLOWED_ACCOUNT_IP = 4; //Allows up to N concurrent IP's for an account. IP's remains linked to an account longer the more times it's used to login.
public static final int MAX_ALLOWED_ACCOUNT_HWID = 4; //Allows up to N concurrent HWID's for an account. HWID's remains linked to an account longer the more times it's used to login.
public static final int MAX_ACCOUNT_LOGIN_ATTEMPT = 15; //After N tries on an account, login on that account gets disabled for a short period.
public static final int LOGIN_ATTEMPT_DURATION = 120; //Period in seconds the login attempt remains registered on the system.
@@ -77,7 +77,7 @@ public class ServerConstants {
public static final boolean USE_ENFORCE_HPMP_SWAP = false; //Forces players to reuse stats (via AP Resetting) located on HP/MP pool only inside the HP/MP stats.
public static final boolean USE_ENFORCE_MOB_LEVEL_RANGE = true; //Players N levels below the killed mob will gain no experience from defeating it.
public static final boolean USE_ENFORCE_JOB_LEVEL_RANGE = false;//Caps the player level on the minimum required to advance their current jobs.
public static final boolean USE_ENFORCE_OWL_SUGGESTIONS = false;//Forces the Owl of Minerva to always display the defined item array on GameConstants.OWL_DATA instead of those featured by the players.
public static final boolean USE_ENFORCE_ITEM_SUGGESTION = false;//Forces the Owl of Minerva and the Cash Shop to always display the defined item array instead of those featured by the players.
public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = true;//Forces players to not sell CASH items via merchants.
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = true; //Forces players to not sell pets via merchants. (since non-named pets gets dirty name and other possible DB-related issues)
public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints.
@@ -87,11 +87,15 @@ public class ServerConstants {
public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped.
public static final boolean USE_ERASE_PET_ON_EXPIRATION = false;//Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll.
public static final boolean USE_BUFF_MOST_SIGNIFICANT = true; //When applying buffs, the player will stick with the highest stat boost among the listed, rather than overwriting stats.
public static final boolean USE_BUFF_EVERLASTING = false; //Every applied buff on players holds expiration time so high it'd be considered permanent. Suggestion thanks to Vcoc.
public static final boolean USE_MULTIPLE_SAME_EQUIP_DROP = true;//Enables multiple drops by mobs of the same equipment, number of possible drops based on the quantities provided at the drop data.
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_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.
//Events/PQs Configuration
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_ENABLE_RECALL_EVENT = true; //Enables a disconnected player to reaccess the last event instance they were in before logging out. Recall only works if the event isn't cleared or disposed yet. Suggestion thanks to Alisson (Goukken).
//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.
@@ -178,12 +182,19 @@ public class ServerConstants {
//Quest Configuration
public static final boolean USE_QUEST_RATE = false; //Exp/Meso gained by quests uses fixed server exp/meso rate times quest rate as multiplier, instead of player rates.
public static final int FAME_GAIN_MIN_HOUR_INTERVAL = 24; //Minimum time interval in hours the repeatable quest must have to be accounted for the "fame gain by quest".
public static final int FAME_GAIN_BY_QUEST = 4; //Fame gain each N quest completes, set 0 to disable.
//Quest Points Configuration
public static final int QUEST_POINT_REPEATABLE_INTERVAL = 24;//Minimum interval between repeatable quest completions for quest points to be awarded.
public static final int QUEST_POINT_REQUIREMENT = 16; //Exchange factor between N quest points to +1 fame, set 0 to disable the entire quest point mechanism.
public static final int QUEST_POINT_PER_QUEST_COMPLETE = 4; //Each completed quest awards N quest points, set 0 to disable.
public static final int QUEST_POINT_PER_EVENT_CLEAR = 1; //Each completed event instance awards N quest points, set 0 to disable.
//Guild Configuration
public static final int CREATE_GUILD_COST = 1500000;
public static final int CHANGE_EMBLEM_COST = 5000000;
public static final int EXPAND_GUILD_BASE_COST = 500000;
public static final int EXPAND_GUILD_TIER_COST = 1000000;
public static final int EXPAND_GUILD_MAX_COST = 5000000;
//Equipment Configuration
public static final boolean USE_EQUIPMNT_LVLUP_SLOTS = true;//Equips can upgrade slots at level up.
+2
View File
@@ -59,6 +59,7 @@ import net.server.guild.MapleGuild;
import net.server.guild.MapleGuildCharacter;
import net.server.worker.CharacterDiseaseWorker;
import net.server.worker.CouponWorker;
import net.server.worker.EventRecallCoordinatorWorker;
import net.server.worker.LoginCoordinatorWorker;
import net.server.worker.LoginStorageWorker;
import net.server.worker.RankingCommandWorker;
@@ -841,6 +842,7 @@ public class Server {
tMan.register(new RankingCommandWorker(), 5 * 60 * 1000, 5 * 60 * 1000);
tMan.register(new RankingLoginWorker(), ServerConstants.RANKING_INTERVAL, timeLeft);
tMan.register(new LoginCoordinatorWorker(), 60 * 60 * 1000, timeLeft);
tMan.register(new EventRecallCoordinatorWorker(), 60 * 60 * 1000, timeLeft);
tMan.register(new LoginStorageWorker(), 2 * 60 * 1000, 2 * 60 * 1000);
long timeToTake = System.currentTimeMillis();
@@ -70,6 +70,7 @@ public enum MonitoredLockType {
WORLD_PSHOPS,
WORLD_MERCHS,
WORLD_MAPOBJS,
WORLD_SUGGEST,
EIM,
EIM_PARTY,
EIM_SCRIPT,
@@ -53,14 +53,14 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.enableActions());
return;
}
final int action = slea.readByte();
final int action = slea.readByte();
if (action == 0x03 || action == 0x1E) {
slea.readByte();
final int useNX = slea.readInt();
final int snCS = slea.readInt();
CashItem cItem = CashItemFactory.getItem(snCS);
if (cItem == null || !cItem.isOnSale() || cs.getCash(useNX) < cItem.getPrice()) {
if (!canBuy(cItem, cs.getCash(useNX))) {
FilePrinter.printError(FilePrinter.ITEM, "Denied to sell cash item with SN " + cItem.getSN());
c.announce(MaplePacketCreator.enableActions());
return;
@@ -87,7 +87,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
}
c.announce(MaplePacketCreator.showBoughtCashPackage(cashPackage, c.getAccID()));
}
cs.gainCash(useNX, -cItem.getPrice());
cs.gainCash(useNX, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
} else if (action == 0x04) {//TODO check for gender
int birthday = slea.readInt();
@@ -113,7 +113,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
}
cs.gift(Integer.parseInt(recipient.get("id")), chr.getName(), message, cItem.getSN());
c.announce(MaplePacketCreator.showGiftSucceed(recipient.get("name"), cItem));
cs.gainCash(4, -cItem.getPrice());
cs.gainCash(4, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
try {
chr.sendNote(recipient.get("name"), chr.getName() + " has sent you a gift! Go check out the Cash Shop.", (byte) 0); //fame or not
@@ -156,7 +156,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
}
if (chr.gainSlots(type, 8, false)) {
c.announce(MaplePacketCreator.showBoughtInventorySlots(type, chr.getSlots(type)));
cs.gainCash(cash, -cItem.getPrice());
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
}
}
@@ -186,7 +186,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
}
if (chr.getStorage().gainSlots(8)) {
c.announce(MaplePacketCreator.showBoughtStorageSlots(chr.getStorage().getSlots()));
cs.gainCash(cash, -cItem.getPrice());
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
}
}
@@ -202,7 +202,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
if (c.gainCharacterSlot()) {
c.announce(MaplePacketCreator.showBoughtCharacterSlot(c.getCharacterSlots()));
cs.gainCash(cash, -cItem.getPrice());
cs.gainCash(cash, cItem, chr.getWorld());
c.announce(MaplePacketCreator.showCash(chr));
} else {
chr.dropMessage(1, "You have already used up all 12 extra character slots.");
@@ -276,7 +276,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
cs.addToInventory(eqp);
c.announce(MaplePacketCreator.showBoughtCashItem(eqp, c.getAccID()));
cs.gift(partner.getId(), chr.getName(), text, eqp.getSN(), (ringid + 1));
cs.gainCash(toCharge, -itemRing.getPrice());
cs.gainCash(toCharge, itemRing, chr.getWorld());
chr.addCrushRing(MapleRing.loadFromDb(ringid));
try {
chr.sendNote(partner.getName(), text, (byte) 1);
@@ -357,7 +357,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
return c.checkBirthDate(cal);
}
public static boolean canBuy(CashItem item, int cash) {
private static boolean canBuy(CashItem item, int cash) {
return item != null && item.isOnSale() && item.getPrice() <= cash;
}
}
@@ -42,24 +42,23 @@ public final class GiveFameHandler extends AbstractMaplePacketHandler {
if (target == null || target.getId() == player.getId() || player.getLevel() < 15) {
return;
} else if (famechange != 1 && famechange != -1) {
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit fame.");
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to fame hack with famechange " + famechange + "\r\n");
c.disconnect(true, false);
return;
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit fame.");
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to fame hack with famechange " + famechange + "\r\n");
c.disconnect(true, false);
return;
}
FameStatus status = player.canGiveFame(target);
if (status == FameStatus.OK || player.isGM()){
if (Math.abs(target.getFame() + famechange) < 30001) {
target.addFame(famechange);
target.updateSingleStat(MapleStat.FAME, target.getFame());
}
if (!player.isGM()) {
player.hasGivenFame(target);
}
c.announce(MaplePacketCreator.giveFameResponse(mode, target.getName(), target.getFame()));
target.getClient().announce(MaplePacketCreator.receiveFame(mode, player.getName()));
if (status == FameStatus.OK) {
if (target.gainFame(famechange, player, mode)) {
if (!player.isGM()) {
player.hasGivenFame(target);
}
} else {
player.message("Could not process the request, since this character currently has the minimum/maximum level of fame.");
}
} else {
c.announce(MaplePacketCreator.giveFameErrorResponse(status == FameStatus.NOT_TODAY ? 3 : 4));
c.announce(MaplePacketCreator.giveFameErrorResponse(status == FameStatus.NOT_TODAY ? 3 : 4));
}
}
}
@@ -77,7 +77,6 @@ public final class NoteActionHandler extends AbstractMaplePacketHandler {
}
if (fame > 0) {
c.getPlayer().gainFame(fame);
c.announce(MaplePacketCreator.getShowFameGain(fame));
}
}
}
@@ -62,7 +62,11 @@ import java.net.InetSocketAddress;
import java.util.Collections;
import java.util.Comparator;
import java.util.Map;
import net.server.coordinator.MapleEventRecallCoordinator;
import net.server.coordinator.MapleSessionCoordinator;
import org.apache.mina.core.session.IoSession;
import server.life.MobSkill;
import scripting.event.EventInstanceManager;
import tools.packets.Wedding;
public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
@@ -79,11 +83,23 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
MapleCharacter player = c.getWorldServer().getPlayerStorage().getCharacterById(cid);
boolean newcomer = false;
if (player == null) {
if(!server.validateCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), cid)) {
IoSession session = c.getSession();
if (!server.validateCharacteridInTransition((InetSocketAddress) session.getRemoteAddress(), cid)) {
c.disconnect(true, false);
return;
}
if (ServerConstants.DETERRED_MULTICLIENT) {
String remoteHwid = MapleSessionCoordinator.getInstance().getGameSessionHwid(session);
if (remoteHwid == null) {
c.disconnect(true, false);
return;
}
session.setAttribute(MapleClient.CLIENT_HWID, remoteHwid);
}
try {
player = MapleCharacter.loadCharFromDB(cid, c, true);
newcomer = true;
@@ -263,7 +279,7 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
c.announce(MaplePacketCreator.requestBuddylistAdd(pendingBuddyRequest.getId(), c.getPlayer().getId(), pendingBuddyRequest.getName()));
}
if(newcomer) {
if (newcomer) {
for(MaplePet pet : player.getPets()) {
if(pet != null)
world.registerPetHunger(player, player.getPetIndex(pet));
@@ -332,6 +348,13 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId()));
}
}
if (newcomer) {
EventInstanceManager eim = MapleEventRecallCoordinator.getInstance().recallEventInstance(cid);
if (eim != null) {
eim.registerPlayer(player);
}
}
}
private static void showDueyNotification(MapleClient c, MapleCharacter player) {
@@ -117,8 +117,8 @@ public final class SpecialMoveHandler extends AbstractMaplePacketHandler {
int gain = lose * (ef.getY() / 100);
chr.setMp(chr.getMp() + gain);
chr.updateSingleStat(MapleStat.MP, chr.getMp());
} else if (skillid == Priest.DISPEL || skillid == SuperGM.HEAL_PLUS_DISPEL) {
slea.skip((skillid == Priest.DISPEL) ? 10 : 11);
} else if (skillid == SuperGM.HEAL_PLUS_DISPEL) {
slea.skip(11);
chr.getMap().broadcastMessage(chr, MaplePacketCreator.showBuffeffect(chr.getId(), skillid, chr.getSkillLevel(skillid)), false);
} else if (skillid % 10000000 == 1004) {
slea.readShort();
@@ -352,7 +352,7 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
} else if (itemType == 523) {
int itemid = slea.readInt();
if(!ServerConstants.USE_ENFORCE_OWL_SUGGESTIONS) c.getWorldServer().addOwlItemSearch(itemid);
if(!ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) c.getWorldServer().addOwlItemSearch(itemid);
player.setOwlSearch(itemid);
List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> hmsAvailable = c.getWorldServer().getAvailableItemBundles(itemid);
if(!hmsAvailable.isEmpty()) remove(c, itemId);
@@ -56,7 +56,7 @@ public final class UseOwlOfMinervaHandler extends AbstractMaplePacketHandler {
}
};
PriorityQueue<Pair<Integer, Integer>> queue = new PriorityQueue<>(10, comparator);
PriorityQueue<Pair<Integer, Integer>> queue = new PriorityQueue<>(Math.max(1, owlSearched.size()), comparator);
for(Pair<Integer, Integer> p : owlSearched) {
queue.add(p);
}
+1 -1
View File
@@ -34,7 +34,7 @@ import net.server.Server;
*/
public class LoginStorage {
ConcurrentHashMap<Integer, List<Long>> loginHistory = new ConcurrentHashMap<>();
private ConcurrentHashMap<Integer, List<Long>> loginHistory = new ConcurrentHashMap<>();
public boolean registerLogin(int accountId) {
List<Long> accHist = loginHistory.putIfAbsent(accountId, new LinkedList<Long>());
@@ -0,0 +1,73 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.coordinator;
import constants.ServerConstants;
import scripting.event.EventInstanceManager;
import java.util.LinkedList;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
/**
*
* @author Ronan
*/
public class MapleEventRecallCoordinator {
private final static MapleEventRecallCoordinator instance = new MapleEventRecallCoordinator();
public static MapleEventRecallCoordinator getInstance() {
return instance;
}
private ConcurrentHashMap<Integer, EventInstanceManager> eventHistory = new ConcurrentHashMap<>();
private static boolean isRecallableEvent(EventInstanceManager eim) {
return eim != null && !eim.isEventDisposed() && !eim.isEventCleared();
}
public EventInstanceManager recallEventInstance(int characterId) {
EventInstanceManager eim = eventHistory.remove(characterId);
return isRecallableEvent(eim) ? eim : null;
}
public void storeEventInstance(int characterId, EventInstanceManager eim) {
if (ServerConstants.USE_ENABLE_RECALL_EVENT && isRecallableEvent(eim)) {
eventHistory.put(characterId, eim);
}
}
public void manageEventInstances() {
if (!eventHistory.isEmpty()) {
List<Integer> toRemove = new LinkedList<>();
for (Entry<Integer, EventInstanceManager> eh : eventHistory.entrySet()) {
if (!isRecallableEvent(eh.getValue())) {
toRemove.add(eh.getKey());
}
}
for (Integer r : toRemove) {
eventHistory.remove(r);
}
}
}
}
@@ -19,6 +19,7 @@
*/
package net.server.coordinator;
import client.MapleClient;
import constants.ServerConstants;
import net.server.Server;
@@ -33,11 +34,14 @@ import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
@@ -58,14 +62,18 @@ public class MapleSessionCoordinator {
REMOTE_LOGGEDIN,
REMOTE_REACHED_LIMIT,
REMOTE_PROCESSING,
REMOTE_NO_MATCH,
MANY_ACCOUNT_ATTEMPTS,
COORDINATOR_ERROR
}
private final LoginStorage loginStorage = new LoginStorage();
private final Set<String> onlineRemoteHosts = new HashSet<>();
private final Set<String> onlineRemoteHwids = new HashSet<>();
private final Map<String, Set<IoSession>> loginRemoteHosts = new HashMap<>();
private final Set<String> pooledRemoteHosts = new HashSet<>();
private final ConcurrentHashMap<String, String> cachedHostHwids = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Long> cachedHostTimeout = new ConcurrentHashMap<>();
private final List<ReentrantLock> poolLock = new ArrayList<>(100);
private MapleSessionCoordinator() {
@@ -74,7 +82,7 @@ public class MapleSessionCoordinator {
}
}
private static long ipExpirationUpdate(int relevance) {
private static long hwidExpirationUpdate(int relevance) {
int degree = 1, i = relevance, subdegree;
while ((subdegree = 5 * degree) <= i) {
i -= subdegree;
@@ -109,59 +117,90 @@ public class MapleSessionCoordinator {
return 3600000 * (baseTime + subdegreeTime);
}
private static void updateAccessAccount(Connection con, String remoteHost, int accountId, int loginRelevance) throws SQLException {
java.sql.Timestamp nextTimestamp = new java.sql.Timestamp(Server.getInstance().getCurrentTime() + ipExpirationUpdate(loginRelevance));
private static void updateAccessAccount(Connection con, String remoteHwid, int accountId, int loginRelevance) throws SQLException {
java.sql.Timestamp nextTimestamp = new java.sql.Timestamp(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(loginRelevance));
if(loginRelevance < Byte.MAX_VALUE) {
loginRelevance++;
}
try (PreparedStatement ps = con.prepareStatement("UPDATE ipaccounts SET relevance = ?, expiresat = ? WHERE accountid = ? AND ip LIKE ?")) {
try (PreparedStatement ps = con.prepareStatement("UPDATE hwidaccounts SET relevance = ?, expiresat = ? WHERE accountid = ? AND hwid LIKE ?")) {
ps.setInt(1, loginRelevance);
ps.setTimestamp(2, nextTimestamp);
ps.setInt(3, accountId);
ps.setString(4, remoteHost);
ps.setString(4, remoteHwid);
ps.executeUpdate();
}
}
private static void registerAccessAccount(Connection con, String remoteHost, int accountId) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("INSERT INTO ipaccounts (accountid, ip, expiresat) VALUES (?, ?, ?)")) {
private static void registerAccessAccount(Connection con, String remoteHwid, int accountId) throws SQLException {
try (PreparedStatement ps = con.prepareStatement("INSERT INTO hwidaccounts (accountid, hwid, expiresat) VALUES (?, ?, ?)")) {
ps.setInt(1, accountId);
ps.setString(2, remoteHost);
ps.setTimestamp(3, new java.sql.Timestamp(Server.getInstance().getCurrentTime() + ipExpirationUpdate(0)));
ps.setString(2, remoteHwid);
ps.setTimestamp(3, new java.sql.Timestamp(Server.getInstance().getCurrentTime() + hwidExpirationUpdate(0)));
ps.executeUpdate();
}
}
private static boolean attemptAccessAccount(String remoteHost, int accountId, boolean routineCheck) {
private static boolean associateHwidAccountIfAbsent(String remoteHwid, int accountId) {
try {
Connection con = DatabaseConnection.getConnection();
int ipCount = 0;
int hwidCount = 0;
try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE * FROM ipaccounts WHERE accountid = ?")) {
try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE hwid FROM hwidaccounts WHERE accountid = ?")) {
ps.setInt(1, accountId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
if (remoteHost.contentEquals(rs.getString("ip"))) {
String rsHwid = rs.getString("hwid");
if (rsHwid.contentEquals(remoteHwid)) {
return false;
}
hwidCount++;
}
}
if (hwidCount < ServerConstants.MAX_ALLOWED_ACCOUNT_HWID) {
registerAccessAccount(con, remoteHwid, accountId);
return true;
}
} finally {
con.close();
}
} catch (SQLException ex) {
ex.printStackTrace();
}
return false;
}
private static boolean attemptAccessAccount(String nibbleHwid, int accountId, boolean routineCheck) {
try {
Connection con = DatabaseConnection.getConnection();
int hwidCount = 0;
try (PreparedStatement ps = con.prepareStatement("SELECT SQL_CACHE * FROM hwidaccounts WHERE accountid = ?")) {
ps.setInt(1, accountId);
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
String rsHwid = rs.getString("hwid");
if (rsHwid.endsWith(nibbleHwid)) {
if (!routineCheck) {
// better update HWID relevance as soon as the login is authenticated
int loginRelevance = rs.getInt("relevance");
updateAccessAccount(con, remoteHost, accountId, loginRelevance);
updateAccessAccount(con, rsHwid, accountId, loginRelevance);
}
return true;
}
ipCount++;
hwidCount++;
}
}
if (ipCount < ServerConstants.MAX_ALLOWED_ACCOUNT_IP) {
if (!routineCheck) {
registerAccessAccount(con, remoteHost, accountId);
}
if (hwidCount < ServerConstants.MAX_ALLOWED_ACCOUNT_HWID) {
return true;
}
} finally {
@@ -218,7 +257,14 @@ public class MapleSessionCoordinator {
}
try {
if (onlineRemoteHosts.contains(remoteHost) || loginRemoteHosts.containsKey(remoteHost)) {
String knownHwid = cachedHostHwids.get(remoteHost);
if (knownHwid != null) {
if (onlineRemoteHwids.contains(knownHwid)) {
return false;
}
}
if (loginRemoteHosts.containsKey(remoteHost)) {
return false;
}
@@ -244,11 +290,16 @@ public class MapleSessionCoordinator {
lrh.remove(session);
if (lrh.isEmpty()) {
loginRemoteHosts.remove(remoteIp);
String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID);
if (nibbleHwid != null) {
onlineRemoteHwids.remove(nibbleHwid);
}
}
}
}
public AntiMulticlientResult attemptSessionLogin(IoSession session, int accountId, boolean routineCheck) {
public AntiMulticlientResult attemptLoginSession(IoSession session, String nibbleHwid, int accountId, boolean routineCheck) {
if (!ServerConstants.DETERRED_MULTICLIENT) return AntiMulticlientResult.SUCCESS;
String remoteHost = getRemoteIp(session);
@@ -289,17 +340,18 @@ public class MapleSessionCoordinator {
}
if (!routineCheck) {
if (onlineRemoteHosts.contains(remoteHost)) {
if (onlineRemoteHwids.contains(nibbleHwid)) {
return AntiMulticlientResult.REMOTE_LOGGEDIN;
}
if (!attemptAccessAccount(remoteHost, accountId, routineCheck)) {
if (!attemptAccessAccount(nibbleHwid, accountId, routineCheck)) {
return AntiMulticlientResult.REMOTE_REACHED_LIMIT;
}
onlineRemoteHosts.add(remoteHost);
session.setAttribute(MapleClient.CLIENT_NIBBLEHWID, nibbleHwid);
onlineRemoteHwids.add(nibbleHwid);
} else {
if (!attemptAccessAccount(remoteHost, accountId, routineCheck)) {
if (!attemptAccessAccount(nibbleHwid, accountId, routineCheck)) {
return AntiMulticlientResult.REMOTE_REACHED_LIMIT;
}
}
@@ -315,15 +367,99 @@ public class MapleSessionCoordinator {
}
}
public void closeSession(IoSession session, Boolean immediately) {
onlineRemoteHosts.remove(getRemoteIp(session));
if (immediately != null) session.close(immediately);
public AntiMulticlientResult attemptGameSession(IoSession session, int accountId, String remoteHwid) {
if (!ServerConstants.DETERRED_MULTICLIENT) return AntiMulticlientResult.SUCCESS;
String remoteHost = getRemoteIp(session);
Lock lock = getCoodinatorLock(remoteHost);
try {
int tries = 0;
while (true) {
if (lock.tryLock()) {
try {
if (pooledRemoteHosts.contains(remoteHost)) {
return AntiMulticlientResult.REMOTE_PROCESSING;
}
pooledRemoteHosts.add(remoteHost);
} finally {
lock.unlock();
}
break;
} else {
if(tries == 2) {
return AntiMulticlientResult.COORDINATOR_ERROR;
}
tries++;
Thread.sleep(1777);
}
}
} catch (Exception e) {
e.printStackTrace();
return AntiMulticlientResult.COORDINATOR_ERROR;
}
try {
String nibbleHwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID);
if (nibbleHwid != null) {
onlineRemoteHwids.remove(nibbleHwid);
if (remoteHwid.endsWith(nibbleHwid)) {
if (!onlineRemoteHwids.contains(remoteHwid)) {
// assumption: after a SUCCESSFUL login attempt, the incoming client WILL receive a new IoSession from the game server
// updated session CLIENT_HWID attribute will be set when the player log in the game
onlineRemoteHwids.add(remoteHwid);
cachedHostHwids.put(remoteHost, remoteHwid);
cachedHostTimeout.put(remoteHost, Server.getInstance().getCurrentTime() + 604800000); // 1 week-time entry
associateHwidAccountIfAbsent(remoteHwid, accountId);
return AntiMulticlientResult.SUCCESS;
} else {
return AntiMulticlientResult.REMOTE_LOGGEDIN;
}
} else {
return AntiMulticlientResult.REMOTE_NO_MATCH;
}
} else {
return AntiMulticlientResult.REMOTE_NO_MATCH;
}
} finally {
lock.lock();
try {
pooledRemoteHosts.remove(remoteHost);
} finally {
lock.unlock();
}
}
}
public void runUpdateIpHistory() {
public void closeSession(IoSession session, Boolean immediately) {
String hwid = (String) session.removeAttribute(MapleClient.CLIENT_HWID);
onlineRemoteHwids.remove(hwid);
hwid = (String) session.removeAttribute(MapleClient.CLIENT_NIBBLEHWID); // making sure to clean up calls to this function on login phase
onlineRemoteHwids.remove(hwid);
if (immediately != null) {
session.close(immediately);
}
}
public String getGameSessionHwid(IoSession session) {
String remoteHost = getRemoteIp(session);
return cachedHostHwids.get(remoteHost);
}
public void runUpdateHwidHistory() {
try {
Connection con = DatabaseConnection.getConnection();
try (PreparedStatement ps = con.prepareStatement("DELETE FROM ipaccounts WHERE expiresat < CURRENT_TIMESTAMP")) {
try (PreparedStatement ps = con.prepareStatement("DELETE FROM hwidaccounts WHERE expiresat < CURRENT_TIMESTAMP")) {
ps.execute();
} finally {
con.close();
@@ -331,6 +467,21 @@ public class MapleSessionCoordinator {
} catch (SQLException ex) {
ex.printStackTrace();
}
long timeNow = Server.getInstance().getCurrentTime();
List<String> toRemove = new LinkedList<>();
for (Entry<String, Long> cht : cachedHostTimeout.entrySet()) {
if (cht.getValue() < timeNow) {
toRemove.add(cht.getKey());
}
}
if (!toRemove.isEmpty()) {
for (String s : toRemove) {
cachedHostHwids.remove(s);
cachedHostTimeout.remove(s);
}
}
}
public void runUpdateLoginHistory() {
+8 -1
View File
@@ -23,6 +23,7 @@ package net.server.guild;
import client.MapleCharacter;
import client.MapleClient;
import constants.ServerConstants;
import java.sql.Connection;
import java.sql.PreparedStatement;
@@ -752,6 +753,12 @@ public class MapleGuild {
}
public static int getIncreaseGuildCost(int size) {
return 500000 * (size - 6) / 6;
int cost = ServerConstants.EXPAND_GUILD_BASE_COST + Math.max(0, (size - 15) / 5) * ServerConstants.EXPAND_GUILD_TIER_COST;
if (size > 30) {
return Math.min(ServerConstants.EXPAND_GUILD_MAX_COST, Math.max(cost, 5000000));
} else {
return cost;
}
}
}
@@ -27,28 +27,64 @@ import java.net.InetSocketAddress;
import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult;
import net.server.world.World;
import org.apache.mina.core.session.IoSession;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class CharSelectedHandler extends AbstractMaplePacketHandler {
private static int parseAntiMulticlientError(AntiMulticlientResult res) {
switch (res) {
case REMOTE_PROCESSING:
return 10;
case REMOTE_LOGGEDIN:
return 7;
case REMOTE_NO_MATCH:
return 17;
case COORDINATOR_ERROR:
return 8;
default:
return 9;
}
}
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
int charId = slea.readInt();
String macs = slea.readMapleAsciiString();
String hwid = slea.readMapleAsciiString();
if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) {
c.announce(MaplePacketCreator.getAfterLoginError(17));
return;
}
c.updateMacs(macs);
c.updateHWID(hwid);
IoSession session = c.getSession();
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid);
if (res != AntiMulticlientResult.SUCCESS) {
c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res)));
return;
}
if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true);
session.close(true);
return;
}
Server server = Server.getInstance();
if(!server.haveCharacterEntry(c.getAccID(), charId)) {
c.getSession().close(true);
session.close(true);
return;
}
@@ -67,7 +103,7 @@ public final class CharSelectedHandler extends AbstractMaplePacketHandler {
server.unregisterLoginState(c);
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
server.setCharacteridInTransition((InetSocketAddress) c.getSession().getRemoteAddress(), charId);
server.setCharacteridInTransition((InetSocketAddress) session.getRemoteAddress(), charId);
try {
c.announce(MaplePacketCreator.getServerIP(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1]), charId));
@@ -6,13 +6,35 @@ import java.net.UnknownHostException;
import net.AbstractMaplePacketHandler;
import net.server.Server;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult;
import net.server.world.World;
import org.apache.mina.core.session.IoSession;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient;
public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
private static int parseAntiMulticlientError(AntiMulticlientResult res) {
switch (res) {
case REMOTE_PROCESSING:
return 10;
case REMOTE_LOGGEDIN:
return 7;
case REMOTE_NO_MATCH:
return 17;
case COORDINATOR_ERROR:
return 8;
default:
return 9;
}
}
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
String pic = slea.readMapleAsciiString();
@@ -20,8 +42,22 @@ public class CharSelectedWithPicHandler extends AbstractMaplePacketHandler {
String macs = slea.readMapleAsciiString();
String hwid = slea.readMapleAsciiString();
if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) {
c.announce(MaplePacketCreator.getAfterLoginError(17));
return;
}
c.updateMacs(macs);
c.updateHWID(hwid);
IoSession session = c.getSession();
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid);
if (res != AntiMulticlientResult.SUCCESS) {
c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res)));
return;
}
if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true);
return;
@@ -31,6 +31,7 @@ import net.MaplePacketHandler;
import net.server.Server;
import tools.BCrypt;
import tools.DatabaseConnection;
import tools.HexTool;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient;
@@ -51,8 +52,10 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
String login = slea.readMapleAsciiString();
String pwd = slea.readMapleAsciiString();
c.setAccountName(login);
int loginok = c.login(login, pwd);
slea.skip(6); // localhost masked the initial part with zeroes...
byte[] hwidNibbles = slea.read(4);
int loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles));
Connection con = null;
PreparedStatement ps = null;
@@ -63,8 +66,8 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
ps = con.prepareStatement("INSERT INTO accounts (name, password, birthday, tempban) VALUES (?, ?, ?, ?);", Statement.RETURN_GENERATED_KEYS); //Jayd: Added birthday, tempban
ps.setString(1, login);
ps.setString(2, BCrypt.hashpw(pwd, BCrypt.gensalt(12)));
ps.setString(3, "2018-06-20"); //Jayd: was added to solve the MySQL 5.7 strict checking (birthday)
ps.setString(4, "2018-06-20"); //Jayd: was added to solve the MySQL 5.7 strict checking (tempban)
ps.setString(3, "2018-06-20"); //Jayd's idea: was added to solve the MySQL 5.7 strict checking (birthday)
ps.setString(4, "2018-06-20"); //Jayd's idea: was added to solve the MySQL 5.7 strict checking (tempban)
ps.executeUpdate();
ResultSet rs = ps.getGeneratedKeys();
@@ -75,7 +78,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
e.printStackTrace();
} finally {
disposeSql(con, ps);
loginok = c.login(login, pwd);
loginok = c.login(login, pwd, HexTool.toCompressedString(hwidNibbles));
}
}
@@ -100,7 +103,7 @@ public final class LoginPasswordHandler implements MaplePacketHandler {
}
Calendar tempban = c.getTempBanCalendar();
if (tempban != null) {
if (tempban.getTimeInMillis() > System.currentTimeMillis()) {
if (tempban.getTimeInMillis() > Calendar.getInstance().getTimeInMillis()) {
c.announce(MaplePacketCreator.getTempBan(tempban.getTimeInMillis(), c.getGReason()));
return;
}
@@ -10,18 +10,54 @@ import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient;
import java.net.InetSocketAddress;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult;
import org.apache.mina.core.session.IoSession;
public final class RegisterPicHandler extends AbstractMaplePacketHandler {
private static int parseAntiMulticlientError(AntiMulticlientResult res) {
switch (res) {
case REMOTE_PROCESSING:
return 10;
case REMOTE_LOGGEDIN:
return 7;
case REMOTE_NO_MATCH:
return 17;
case COORDINATOR_ERROR:
return 8;
default:
return 9;
}
}
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
slea.readByte();
int charId = slea.readInt();
String macs = slea.readMapleAsciiString();
String hwid = slea.readMapleAsciiString();
String hwid = slea.readMapleAsciiString();
if (!hwid.matches("[0-9A-F]{12}_[0-9A-F]{8}")) {
c.announce(MaplePacketCreator.getAfterLoginError(17));
return;
}
c.updateMacs(macs);
c.updateHWID(hwid);
IoSession session = c.getSession();
AntiMulticlientResult res = MapleSessionCoordinator.getInstance().attemptGameSession(session, c.getAccID(), hwid);
if (res != AntiMulticlientResult.SUCCESS) {
c.announce(MaplePacketCreator.getAfterLoginError(parseAntiMulticlientError(res)));
return;
}
if (c.hasBannedMac() || c.hasBannedHWID()) {
c.getSession().close(true);
return;
@@ -0,0 +1,34 @@
/*
This file is part of the HeavenMS MapleStory Server
Copyleft (L) 2016 - 2018 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.worker;
import net.server.coordinator.MapleEventRecallCoordinator;
/**
*
* @author Ronan
*/
public class EventRecallCoordinatorWorker implements Runnable {
@Override
public void run() {
MapleEventRecallCoordinator.getInstance().manageEventInstances();
}
}
@@ -29,6 +29,6 @@ public class LoginCoordinatorWorker implements Runnable {
@Override
public void run() {
MapleSessionCoordinator.getInstance().runUpdateIpHistory();
MapleSessionCoordinator.getInstance().runUpdateHwidHistory();
}
}
+114 -5
View File
@@ -27,6 +27,7 @@ import client.BuddyList.BuddyOperation;
import client.BuddylistEntry;
import client.MapleCharacter;
import client.MapleFamily;
import constants.GameConstants;
import constants.ServerConstants;
import java.sql.Connection;
@@ -53,6 +54,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import java.util.Set;
import java.util.HashSet;
import java.util.PriorityQueue;
import java.util.concurrent.ScheduledFuture;
import scripting.event.EventInstanceManager;
@@ -122,6 +124,11 @@ public class World {
private MonitoredReentrantLock partyLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_PARTY, true);
private Map<Integer, Integer> owlSearched = new LinkedHashMap<>();
private List<Map<Integer, Integer>> cashItemBought = new ArrayList<>(9);
private final ReentrantReadWriteLock suggestLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.WORLD_SUGGEST, true);
private ReadLock suggestRLock = suggestLock.readLock();
private WriteLock suggestWLock = suggestLock.writeLock();
private Map<Integer, Integer> disabledServerMessages = new HashMap<>(); // reuse owl lock
private MonitoredReentrantLock srvMessagesLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.WORLD_SRVMESSAGES);
private ScheduledFuture<?> srvMessagesSchedule;
@@ -166,6 +173,10 @@ public class World {
petUpdate = System.currentTimeMillis();
mountUpdate = petUpdate;
for (int i = 0; i < 9; i++) {
cashItemBought.add(new LinkedHashMap<Integer, Integer>());
}
TimerManager tman = TimerManager.getInstance();
petsSchedule = tman.register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000);
srvMessagesSchedule = tman.register(new ServerMessageWorker(this), 10 * 1000, 10 * 1000);
@@ -1152,7 +1163,7 @@ public class World {
}
public void addOwlItemSearch(Integer itemid) {
srvMessagesLock.lock();
suggestWLock.lock();
try {
Integer cur = owlSearched.get(itemid);
if(cur != null) {
@@ -1161,16 +1172,16 @@ public class World {
owlSearched.put(itemid, 1);
}
} finally {
srvMessagesLock.unlock();
suggestWLock.unlock();
}
}
public List<Pair<Integer, Integer>> getOwlSearchedItems() {
if(ServerConstants.USE_ENFORCE_OWL_SUGGESTIONS) {
if(ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) {
return new ArrayList<>(0);
}
srvMessagesLock.lock();
suggestRLock.lock();
try {
List<Pair<Integer, Integer>> searchCounts = new ArrayList<>(owlSearched.size());
@@ -1180,10 +1191,108 @@ public class World {
return searchCounts;
} finally {
srvMessagesLock.unlock();
suggestRLock.unlock();
}
}
public void addCashItemBought(Integer snid) {
suggestWLock.lock();
try {
Map<Integer, Integer> tabItemBought = cashItemBought.get(snid / 10000000);
Integer cur = tabItemBought.get(snid);
if (cur != null) {
tabItemBought.put(snid, cur + 1);
} else {
tabItemBought.put(snid, 1);
}
} finally {
suggestWLock.unlock();
}
}
private List<List<Pair<Integer, Integer>>> getBoughtCashItems() {
if (ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) {
return new ArrayList<>(0);
}
suggestRLock.lock();
try {
List<List<Pair<Integer, Integer>>> boughtCounts = new ArrayList<>(cashItemBought.size());
for (Map<Integer, Integer> tab : cashItemBought) {
List<Pair<Integer, Integer>> tabItems = new LinkedList<>();
boughtCounts.add(tabItems);
for (Entry<Integer, Integer> e : tab.entrySet()) {
tabItems.add(new Pair<>(e.getKey(), e.getValue()));
}
}
return boughtCounts;
} finally {
suggestRLock.unlock();
}
}
private List<Integer> getMostSellerOnTab(List<Pair<Integer, Integer>> tabSellers) {
List<Integer> tabLeaderboards;
Comparator<Pair<Integer, Integer>> comparator = new Comparator<Pair<Integer, Integer>>() { // descending order
@Override
public int compare(Pair<Integer, Integer> p1, Pair<Integer, Integer> p2) {
return p2.getRight().compareTo(p1.getRight());
}
};
PriorityQueue<Pair<Integer, Integer>> queue = new PriorityQueue<>(Math.max(1, tabSellers.size()), comparator);
for(Pair<Integer, Integer> p : tabSellers) {
queue.add(p);
}
tabLeaderboards = new LinkedList<>();
for(int i = 0; i < Math.min(tabSellers.size(), 5); i++) {
tabLeaderboards.add(queue.remove().getLeft());
}
return tabLeaderboards;
}
public List<List<Integer>> getMostSellerCashItems() {
List<List<Pair<Integer, Integer>>> mostSellers = this.getBoughtCashItems();
List<List<Integer>> cashLeaderboards = new ArrayList<>(9);
List<Integer> tabLeaderboards;
List<Integer> allLeaderboards = null;
for(List<Pair<Integer, Integer>> tabSellers : mostSellers) {
if (tabSellers.size() < 5) {
if (allLeaderboards == null) {
List<Pair<Integer, Integer>> allSellers = new LinkedList<>();
for (List<Pair<Integer, Integer>> tabItems : mostSellers) {
allSellers.addAll(tabItems);
}
allLeaderboards = getMostSellerOnTab(allSellers);
}
tabLeaderboards = new LinkedList<>();
if (allLeaderboards.size() < 5) {
for(int i : GameConstants.CASH_DATA) {
tabLeaderboards.add(i);
}
} else {
tabLeaderboards.addAll(allLeaderboards);
}
} else {
tabLeaderboards = getMostSellerOnTab(tabSellers);
}
cashLeaderboards.add(tabLeaderboards);
}
return cashLeaderboards;
}
public void registerPetHunger(MapleCharacter chr, byte petSlot) {
if(chr.isGM() && ServerConstants.GM_PETS_NEVER_HUNGRY || ServerConstants.PETS_NEVER_HUNGRY) {
return;
+2 -2
View File
@@ -54,6 +54,7 @@ import tools.MaplePacketCreator;
import client.MapleCharacter;
import client.MapleClient;
import client.MapleQuestStatus;
import client.MapleStat;
import client.SkillFactory;
import client.inventory.Equip;
import client.inventory.Item;
@@ -581,8 +582,7 @@ public class AbstractPlayerInteraction {
}
public void gainFame(int delta) {
c.getPlayer().addFame(delta);
c.announce(MaplePacketCreator.getShowFameGain(delta));
getPlayer().gainFame(delta);
}
public void changeMusic(String songName) {
+12 -1
View File
@@ -65,6 +65,7 @@ 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 net.server.coordinator.MapleEventRecallCoordinator;
import scripting.AbstractPlayerInteraction;
import server.MapleItemInformationProvider;
import server.life.MapleLifeFactory;
@@ -599,6 +600,8 @@ public class EventInstanceManager {
} catch (ScriptException | NoSuchMethodException ex) {
ex.printStackTrace();
}
MapleEventRecallCoordinator.getInstance().storeEventInstance(chr.getId(), this);
}
/**
@@ -862,7 +865,7 @@ public class EventInstanceManager {
}
public void clearPQ() {
try {
try {
sL.lock();
try {
em.getIv().invokeFunction("clearPQ", this);
@@ -1157,6 +1160,10 @@ public class EventInstanceManager {
public final void setEventCleared() {
eventCleared = true;
for (MapleCharacter chr : getPlayers()) {
chr.awardQuestPoint(ServerConstants.QUEST_POINT_PER_EVENT_CLEAR);
}
sL.lock();
try {
em.disposeInstance(name);
@@ -1171,6 +1178,10 @@ public class EventInstanceManager {
return eventCleared;
}
public final boolean isEventDisposed() {
return disposed;
}
private boolean isEventTeamLeaderOn() {
for(MapleCharacter chr: getPlayers()) {
if(chr.getId() == getLeaderId()) return true;
@@ -285,6 +285,7 @@ public class NPCConversationManager extends AbstractPlayerInteraction {
return getPlayer().getLevel();
}
@Override
public void showEffect(String effect) {
getPlayer().getMap().broadcastMessage(MaplePacketCreator.environmentChange(effect, 3));
}
+1 -1
View File
@@ -42,7 +42,7 @@ public class QuestScriptManager extends AbstractScriptManager {
private static QuestScriptManager instance = new QuestScriptManager();
public synchronized static QuestScriptManager getInstance() {
public static QuestScriptManager getInstance() {
return instance;
}
@@ -44,7 +44,7 @@ public class ReactorScriptManager extends AbstractScriptManager {
private static ReactorScriptManager instance = new ReactorScriptManager();
public synchronized static ReactorScriptManager getInstance() {
public static ReactorScriptManager getInstance() {
return instance;
}
+12 -5
View File
@@ -32,6 +32,8 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.Lock;
import net.server.Server;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import provider.MapleData;
@@ -107,18 +109,18 @@ public class CashShop {
if (ItemConstants.EXPIRING_ITEMS) {
if(period == 1) {
if(itemId == 5211048 || itemId == 5360042) { // 4 Hour 2X coupons, the period is 1, but we don't want them to last a day.
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 4));
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 4));
/*
} else if(itemId == 5211047 || itemId == 5360014) { // 3 Hour 2X coupons, unused as of now
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 3));
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 3));
*/
} else if(itemId == 5211060) { // 2 Hour 3X coupons.
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 2));
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 2));
} else {
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24));
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 24));
}
} else {
item.setExpiration(System.currentTimeMillis() + (1000 * 60 * 60 * 24 * period));
item.setExpiration(Server.getInstance().getCurrentTime() + (1000 * 60 * 60 * 24 * period));
}
}
@@ -353,6 +355,11 @@ public class CashShop {
break;
}
}
public void gainCash(int type, CashItem buyItem, int world) {
gainCash(type, -buyItem.getPrice());
if(!ServerConstants.USE_ENFORCE_ITEM_SUGGESTION) Server.getInstance().getWorld(world).addCashItemBought(buyItem.getSN());
}
public boolean isOpened() {
return opened;
+69 -18
View File
@@ -97,6 +97,8 @@ public class MapleItemInformationProvider {
protected Map<Integer, MapleStatEffect> itemEffects = new HashMap<>();
protected Map<Integer, Map<String, Integer>> equipStatsCache = new HashMap<>();
protected Map<Integer, Equip> equipCache = new HashMap<>();
protected Map<Integer, MapleData> equipLevelInfoCache = new HashMap<>();
protected Map<Integer, Integer> equipMaxLevelCache = new HashMap<>();
protected Map<Integer, Integer> wholePriceCache = new HashMap<>();
protected Map<Integer, Double> unitPriceCache = new HashMap<>();
protected Map<Integer, Integer> projectileWatkCache = new HashMap<>();
@@ -1217,7 +1219,7 @@ public class MapleItemInformationProvider {
return ret;
}
public boolean isDropRestricted(int itemId) {
public boolean isLootRestricted(int itemId) {
if (dropRestrictionCache.containsKey(itemId)) {
return dropRestrictionCache.get(itemId);
}
@@ -1227,16 +1229,17 @@ public class MapleItemInformationProvider {
MapleData data = getItemData(itemId);
bRestricted = MapleDataTool.getIntConvert("info/tradeBlock", data, 0) == 1;
if (!bRestricted) {
bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1;
}
if (!bRestricted) {
bRestricted = MapleDataTool.getIntConvert("info/quest", data, 0) == 1;
bRestricted = MapleDataTool.getIntConvert("info/accountSharable", data, 0) == 1;
}
}
dropRestrictionCache.put(itemId, bRestricted);
return bRestricted;
}
public boolean isDropRestricted(int itemId) {
return isLootRestricted(itemId) || isQuestItem(itemId);
}
public boolean isPickupRestricted(int itemId) {
if (pickupRestrictionCache.containsKey(itemId)) {
@@ -1674,24 +1677,72 @@ public class MapleItemInformationProvider {
return true;
}
public ArrayList<Pair<Integer, String>> getItemDataByName(String name)
{
public ArrayList<Pair<Integer, String>> getItemDataByName(String name) {
ArrayList<Pair<Integer, String>> ret = new ArrayList<>();
for (Pair<Integer, String> itemPair : MapleItemInformationProvider.getInstance().getAllItems()) {
if (itemPair.getRight().toLowerCase().contains(name.toLowerCase())) {
ret.add(itemPair);
}
}
return ret;
for (Pair<Integer, String> itemPair : MapleItemInformationProvider.getInstance().getAllItems()) {
if (itemPair.getRight().toLowerCase().contains(name.toLowerCase())) {
ret.add(itemPair);
}
}
return ret;
}
private MapleData getEquipLevelInfo(int itemId) {
MapleData equipLevelData = equipLevelInfoCache.get(itemId);
if (equipLevelData == null) {
if (equipLevelInfoCache.containsKey(itemId)) return null;
MapleData iData = getItemData(itemId);
if (iData != null) {
MapleData data = iData.getChildByPath("info/level");
if (data != null) {
equipLevelData = data.getChildByPath("info");
}
}
equipLevelInfoCache.put(itemId, equipLevelData);
}
return equipLevelData;
}
public int getEquipLevel(int itemId, boolean getMaxLevel) {
Integer eqLevel = equipMaxLevelCache.get(itemId);
if (eqLevel == null) {
eqLevel = 1; // greater than 1 means that it was supposed to levelup on GMS
MapleData data = getEquipLevelInfo(itemId);
if (data != null) {
if (getMaxLevel) {
int curLevel = 1;
while (true) {
MapleData data2 = data.getChildByPath(Integer.toString(curLevel));
if (data2 == null || data2.getChildren().size() <= 1) {
eqLevel = curLevel;
equipMaxLevelCache.put(itemId, eqLevel);
break;
}
curLevel++;
}
} else {
MapleData data2 = data.getChildByPath("1");
if (data2 != null && data2.getChildren().size() > 1) {
eqLevel = 2;
}
}
}
}
return eqLevel;
}
public List<Pair<String, Integer>> getItemLevelupStats(int itemId, int level) {
List<Pair<String, Integer>> list = new LinkedList<>();
MapleData data = getItemData(itemId);
MapleData data1 = data.getChildByPath("info").getChildByPath("level");
if (data1 != null) {
MapleData data2 = data1.getChildByPath("info").getChildByPath(Integer.toString(level));
MapleData data = getEquipLevelInfo(itemId);
if (data != null) {
MapleData data2 = data.getChildByPath(Integer.toString(level));
if (data2 != null) {
for (MapleData da : data2.getChildren()) {
if (Math.random() < 0.9) {
+13 -9
View File
@@ -30,7 +30,7 @@ import java.util.Collections;
import java.util.List;
import java.util.Map;
import net.server.world.MaplePartyCharacter;
import net.server.Server;
import provider.MapleData;
import provider.MapleDataTool;
import server.life.MapleMonster;
@@ -997,11 +997,15 @@ public class MapleStatEffect {
return bounds;
}
public int getBuffLocalDuration() {
return !ServerConstants.USE_BUFF_EVERLASTING ? duration : Integer.MAX_VALUE;
}
public void silentApplyBuff(MapleCharacter chr, long localStartTime) {
int localDuration = duration;
int localDuration = getBuffLocalDuration();
localDuration = alchemistModifyVal(chr, localDuration, false);
//CancelEffectAction cancelAction = new CancelEffectAction(chr, this, starttime);
//ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + localDuration) - System.currentTimeMillis()));
//ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + localDuration) - Server.getInstance().getCurrentTime()));
chr.registerEffect(this, localStartTime, localStartTime + localDuration, true);
SummonMovementType summonMovementType = getSummonMovementType();
@@ -1021,17 +1025,17 @@ public class MapleStatEffect {
final List<Pair<MapleBuffStat, Integer>> stat = Collections.singletonList(new Pair<>(MapleBuffStat.ARAN_COMBO, combo));
applyto.announce(MaplePacketCreator.giveBuff(sourceid, 99999, stat));
final long starttime = System.currentTimeMillis();
final long starttime = Server.getInstance().getCurrentTime();
// final CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime);
// final ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + 99999) - System.currentTimeMillis()));
// final ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, ((starttime + 99999) - Server.getInstance().getCurrentTime()));
applyto.registerEffect(this, starttime, Long.MAX_VALUE, false);
}
public void updateBuffEffect(MapleCharacter target, List<Pair<MapleBuffStat, Integer>> activeStats, long starttime) {
int localDuration = duration;
int localDuration = getBuffLocalDuration();
localDuration = alchemistModifyVal(target, localDuration, false);
long leftDuration = (starttime + localDuration) - System.currentTimeMillis();
long leftDuration = (starttime + localDuration) - Server.getInstance().getCurrentTime();
if(leftDuration > 0) {
target.announce(MaplePacketCreator.giveBuff((skill ? sourceid : -sourceid), (int)leftDuration, activeStats));
}
@@ -1043,7 +1047,7 @@ public class MapleStatEffect {
}
List<Pair<MapleBuffStat, Integer>> localstatups = statups;
int localDuration = duration;
int localDuration = getBuffLocalDuration();
int localsourceid = sourceid;
int seconds = localDuration / 1000;
MapleMount givemount = null;
@@ -1155,7 +1159,7 @@ public class MapleStatEffect {
}
}
long starttime = System.currentTimeMillis();
long starttime = Server.getInstance().getCurrentTime();
//CancelEffectAction cancelAction = new CancelEffectAction(applyto, this, starttime);
//ScheduledFuture<?> schedule = TimerManager.getInstance().schedule(cancelAction, localDuration);
applyto.registerEffect(this, starttime, starttime + localDuration, false);
-1
View File
@@ -32,7 +32,6 @@ import client.status.MonsterStatus;
import constants.GameConstants;
import java.util.LinkedList;
import java.util.Map;
import server.TimerManager;
import tools.Randomizer;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
+2 -2
View File
@@ -48,9 +48,9 @@ public class MapleLootInventory {
}
}
public boolean hasItem(int itemid, int quantity) {
public int hasItem(int itemid, int quantity) {
Integer itemQty = items.get(itemid);
return itemQty != null && itemQty >= quantity;
return itemQty == null ? 0 : itemQty >= quantity ? 2 : itemQty > 0 ? 1 : 0;
}
}
+15 -10
View File
@@ -34,25 +34,30 @@ import server.quest.MapleQuest;
*/
public class MapleLootManager {
private static boolean needQuestItem(MonsterDropEntry dropEntry, MapleQuest quest, MapleCharacter chr, MapleLootInventory chrInv) {
if (chr.getQuestStatus(dropEntry.questid) != 1) return false;
return !chrInv.hasItem(dropEntry.itemId, quest.getItemAmountNeeded(dropEntry.itemId));
}
private static boolean isRelevantDrop(MonsterDropEntry dropEntry, List<MapleCharacter> partyMembers, List<MapleLootInventory> partyInv) {
MapleQuest quest = MapleQuest.getInstance(dropEntry.questid);
boolean restricted = MapleItemInformationProvider.getInstance().isDropRestricted(dropEntry.itemId);
int qItemAmount = quest != null ? quest.getItemAmountNeeded(dropEntry.itemId) : 0;
boolean restricted = MapleItemInformationProvider.getInstance().isLootRestricted(dropEntry.itemId);
for (int i = 0; i < partyMembers.size(); i++) {
MapleLootInventory chrInv = partyInv.get(i);
if(restricted && chrInv.hasItem(dropEntry.itemId, 1)) {
if (dropEntry.questid > 0) {
if (partyMembers.get(i).getQuestStatus(dropEntry.questid) != 1) {
continue;
}
int qItemStatus = chrInv.hasItem(dropEntry.itemId, qItemAmount);
if (qItemStatus == 2) {
continue;
} else if (restricted && qItemStatus == 1) {
continue;
}
} else if (restricted && chrInv.hasItem(dropEntry.itemId, 1) > 0) {
continue;
}
if(dropEntry.questid <= 0 || needQuestItem(dropEntry, quest, partyMembers.get(i), chrInv)) {
return true;
}
return true;
}
return false;
+1 -1
View File
@@ -242,7 +242,7 @@ public class MapleHiredMerchant extends AbstractMapleMapObject {
if (c.getPlayer().getMeso() >= price) {
if (canBuy(c, newItem)) {
c.getPlayer().gainMeso(-price, false);
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to vcoc
if(ServerConstants.USE_ANNOUNCE_SHOPITEMSOLD) announceItemSold(newItem, price); // idea thanks to Vcoc
synchronized (sold) {
sold.add(new SoldItem(c.getPlayer().getName(), pItem.getItem().getItemId(), newItem.getQuantity(), price));
+2 -2
View File
@@ -1144,11 +1144,11 @@ public class MapleMap {
return character;
}
public List<MapleCharacter> getPlayersInRange(Rectangle box, List<MapleCharacter> chrList) {
public List<MapleCharacter> getPlayersInRange(Rectangle box, List<MapleCharacter> targets) {
List<MapleCharacter> character = new LinkedList<>();
chrRLock.lock();
try {
for (MapleCharacter chr : chrList) {
for (MapleCharacter chr : targets) {
if (characters.contains(chr)) {
if (box.contains(chr.getPosition())) {
character.add(chr);
+5 -6
View File
@@ -227,7 +227,7 @@ public class MapleQuest {
if(!repeatable) return false;
IntervalRequirement ir = (IntervalRequirement) startReqs.get(MapleQuestRequirementType.INTERVAL);
return ir.getInterval() < ServerConstants.FAME_GAIN_MIN_HOUR_INTERVAL * 60 * 60 * 1000;
return ir.getInterval() < ServerConstants.QUEST_POINT_REPEATABLE_INTERVAL * 60 * 60 * 1000;
}
public boolean canStartWithoutRequirements(MapleCharacter c) {
@@ -369,12 +369,11 @@ public class MapleQuest {
public int getItemAmountNeeded(int itemid) {
MapleQuestRequirement req = completeReqs.get(MapleQuestRequirementType.ITEM);
if(req == null)
return 0;
if(req == null)
return 0;
ItemRequirement ireq = (ItemRequirement) req;
return ireq.getItemAmountNeeded(itemid);
ItemRequirement ireq = (ItemRequirement) req;
return ireq.getItemAmountNeeded(itemid);
}
public int getMobAmountNeeded(int mid) {
+1 -3
View File
@@ -50,8 +50,6 @@ public class FameAction extends MapleQuestAction {
@Override
public void run(MapleCharacter chr, Integer extSelection) {
chr.addFame(fame);
chr.updateSingleStat(MapleStat.FAME, chr.getFame());
chr.announce(MaplePacketCreator.getShowFameGain(fame));
chr.gainFame(fame);
}
}
+8
View File
@@ -40,6 +40,14 @@ public class HexTool {
}
return hexed.substring(0, hexed.length() - 1);
}
public static String toCompressedString(byte[] bytes) {
StringBuilder hexed = new StringBuilder();
for (int i = 0; i < bytes.length; i++) {
hexed.append(toString(bytes[i]));
}
return hexed.substring(0, hexed.length());
}
public static byte[] getByteArrayFromHexString(String hex) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
+38 -44
View File
@@ -758,6 +758,8 @@ public class MaplePacketCreator {
*
* @param serverId
* @param serverName The name of the server.
* @param flag
* @param eventmsg
* @param channelLoad Load of the channel - 1200 seems to be max.
* @return The server info packet.
*/
@@ -777,8 +779,11 @@ public class MaplePacketCreator {
for (Channel ch : channelLoad) {
mplew.writeMapleAsciiString(serverName + "-" + ch.getId());
mplew.writeInt(ch.getChannelCapacity());
mplew.write(1);
mplew.writeShort(ch.getId() - 1);
// thanks GabrielSin for this channel packet structure part
mplew.write(1);// nWorldID
mplew.write(ch.getId() - 1);// nChannelID
mplew.writeBool(false);// bAdultChannel
}
mplew.writeShort(0);
return mplew.getPacket();
@@ -1042,8 +1047,8 @@ public class MaplePacketCreator {
mplew.write(spawnPoint);
mplew.writeShort(chr.getHp());
mplew.writeBool(false);
mplew.writeLong(getTime(System.currentTimeMillis()));
mplew.writeShort(0);
mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
mplew.writeInt(0);
return mplew.getPacket();
}
@@ -1059,8 +1064,8 @@ public class MaplePacketCreator {
mplew.writeBool(true);
mplew.writeInt(spawnPosition.x); // spawn position placement thanks to Arnah (Vertisy)
mplew.writeInt(spawnPosition.y);
mplew.writeLong(getTime(System.currentTimeMillis()));
mplew.writeShort(0);
mplew.writeLong(getTime(Server.getInstance().getCurrentTime()));
mplew.writeInt(0);
return mplew.getPacket();
}
@@ -2461,27 +2466,27 @@ public class MaplePacketCreator {
mplew.write(mod.getInventoryType());
mplew.writeShort(mod.getMode() == 2 ? mod.getOldPosition() : mod.getPosition());
switch (mod.getMode()) {
case 0: {//add item
addItemInfo(mplew, mod.getItem(), true);
break;
}
case 1: {//update quantity
mplew.writeShort(mod.getQuantity());
break;
}
case 2: {//move
mplew.writeShort(mod.getPosition());
if (mod.getPosition() < 0 || mod.getOldPosition() < 0) {
addMovement = mod.getOldPosition() < 0 ? 1 : 2;
case 0: {//add item
addItemInfo(mplew, mod.getItem(), true);
break;
}
break;
}
case 3: {//remove
if (mod.getPosition() < 0) {
addMovement = 2;
case 1: {//update quantity
mplew.writeShort(mod.getQuantity());
break;
}
case 2: {//move
mplew.writeShort(mod.getPosition());
if (mod.getPosition() < 0 || mod.getOldPosition() < 0) {
addMovement = mod.getOldPosition() < 0 ? 1 : 2;
}
break;
}
case 3: {//remove
if (mod.getPosition() < 0) {
addMovement = 2;
}
break;
}
break;
}
}
mod.clear();
}
@@ -7527,27 +7532,16 @@ public class MaplePacketCreator {
}
mplew.skip(121);
List<List<Integer>> mostSellers = c.getWorldServer().getMostSellerCashItems();
for (int i = 1; i <= 8; i++) {
List<Integer> mostSellersTab = mostSellers.get(i);
for (int j = 0; j < 2; j++) {
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50200004);
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50200069);
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50200117);
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50100008);
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(50000047);
for (Integer snid : mostSellersTab) {
mplew.writeInt(i);
mplew.writeInt(j);
mplew.writeInt(snid);
}
}
}