Merge branch 'master' of https://github.com/ronancpl/HeavenMS into credits_update

This commit is contained in:
ronancpl
2019-11-14 22:35:43 -03:00
864 changed files with 20650 additions and 11188 deletions

View File

@@ -26,5 +26,6 @@ package client;
public interface AbstractCharacterListener {
public void onHpChanged(int oldHp);
public void onHpmpPoolUpdate();
public void onStatUpdate();
public void onAnnounceStatPoolUpdate();
}

View File

@@ -19,18 +19,19 @@
*/
package client;
import constants.GameConstants;
import constants.ServerConstants;
import config.YamlConfig;
import constants.game.GameConstants;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.ReadLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReadLock;
import net.server.audit.locks.MonitoredReentrantReadWriteLock;
import net.server.audit.locks.MonitoredWriteLock;
import net.server.audit.locks.factory.MonitoredReadLockFactory;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.audit.locks.factory.MonitoredWriteLockFactory;
import server.maps.AbstractAnimatedMapleMapObject;
import server.maps.MapleMap;
@@ -50,13 +51,13 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
protected Map<MapleStat, Integer> statUpdates = new HashMap<>();
protected Lock effLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CHARACTER_EFF, true);
protected ReadLock statRlock;
protected WriteLock statWlock;
protected MonitoredReadLock statRlock;
protected MonitoredWriteLock statWlock;
protected AbstractMapleCharacterObject() {
ReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.CHARACTER_STA, true);
statRlock = locks.readLock();
statWlock = locks.writeLock();
MonitoredReentrantReadWriteLock locks = new MonitoredReentrantReadWriteLock(MonitoredLockType.CHARACTER_STA, true);
statRlock = MonitoredReadLockFactory.createLock(locks);
statWlock = MonitoredWriteLockFactory.createLock(locks);
for (int i = 0; i < remainingSp.length; i++) {
remainingSp[i] = 0;
@@ -220,6 +221,10 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
listener.onHpmpPoolUpdate();
}
private void dispatchStatUpdated() {
listener.onStatUpdate();
}
private void dispatchStatPoolUpdateAnnounced() {
listener.onAnnounceStatPoolUpdate();
}
@@ -299,6 +304,7 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
try {
statUpdates.clear();
boolean poolUpdate = false;
boolean statUpdate = false;
if (hpMpPool != null) {
short newHp = (short) (hpMpPool >> 48);
@@ -370,7 +376,7 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
statUpdates.put(MapleStat.AVAILABLEAP, remainingAp);
}
poolUpdate = true; // recalc stats
statUpdate = true;
}
if (newSp != null) {
@@ -385,6 +391,10 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
if (poolUpdate) {
dispatchHpmpPoolUpdated();
}
if (statUpdate) {
dispatchStatUpdated();
}
if (!silent) {
dispatchStatPoolUpdateAnnounced();
@@ -626,19 +636,19 @@ public abstract class AbstractMapleCharacterObject extends AbstractAnimatedMaple
}
int newStr = str + deltaStr, newDex = dex + deltaDex, newInt = int_ + deltaInt, newLuk = luk + deltaLuk;
if (newStr < 4 && deltaStr != Short.MIN_VALUE || newStr > ServerConstants.MAX_AP) {
if (newStr < 4 && deltaStr != Short.MIN_VALUE || newStr > YamlConfig.config.server.MAX_AP) {
return false;
}
if (newDex < 4 && deltaDex != Short.MIN_VALUE || newDex > ServerConstants.MAX_AP) {
if (newDex < 4 && deltaDex != Short.MIN_VALUE || newDex > YamlConfig.config.server.MAX_AP) {
return false;
}
if (newInt < 4 && deltaInt != Short.MIN_VALUE || newInt > ServerConstants.MAX_AP) {
if (newInt < 4 && deltaInt != Short.MIN_VALUE || newInt > YamlConfig.config.server.MAX_AP) {
return false;
}
if (newLuk < 4 && deltaLuk != Short.MIN_VALUE || newLuk > ServerConstants.MAX_AP) {
if (newLuk < 4 && deltaLuk != Short.MIN_VALUE || newLuk > YamlConfig.config.server.MAX_AP) {
return false;
}

File diff suppressed because it is too large Load Diff

View File

@@ -44,13 +44,13 @@ import java.util.Set;
import java.util.concurrent.Semaphore;
import java.util.concurrent.locks.Lock;
import config.YamlConfig;
import jdk.nashorn.api.scripting.NashornScriptEngine;
import tools.*;
import javax.script.ScriptEngine;
import net.server.Server;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.MapleSessionCoordinator.AntiMulticlientResult;
import net.server.coordinator.session.MapleSessionCoordinator;
import net.server.coordinator.session.MapleSessionCoordinator.AntiMulticlientResult;
import net.server.channel.Channel;
import net.server.guild.MapleGuild;
import net.server.guild.MapleGuildCharacter;
@@ -63,8 +63,7 @@ import net.server.world.World;
import org.apache.mina.core.session.IoSession;
import client.inventory.MapleInventoryType;
import constants.GameConstants;
import constants.ServerConstants;
import constants.game.GameConstants;
import scripting.AbstractPlayerInteraction;
import scripting.event.EventInstanceManager;
import scripting.event.EventManager;
@@ -73,18 +72,16 @@ import scripting.npc.NPCScriptManager;
import scripting.quest.QuestActionManager;
import scripting.quest.QuestScriptManager;
import server.life.MapleMonster;
import server.MapleTrade;
import server.ThreadManager;
import server.maps.*;
import server.quest.MapleQuest;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.coordinator.MapleLoginBypassCoordinator;
import net.server.coordinator.login.MapleLoginBypassCoordinator;
public class MapleClient {
public static final int LOGIN_NOTLOGGEDIN = 0;
public static final int LOGIN_NOTLOGGEDIN = 0;
public static final int LOGIN_SERVER_TRANSITION = 1;
public static final int LOGIN_LOGGEDIN = 2;
public static final String CLIENT_KEY = "CLIENT";
@@ -95,7 +92,7 @@ public class MapleClient {
private MapleAESOFB send;
private MapleAESOFB receive;
private final IoSession session;
private MapleCharacter player;
private MapleCharacter player;
private int channel = 1;
private int accId = -4;
private boolean loggedIn = false;
@@ -106,7 +103,7 @@ public class MapleClient {
private long lastPong;
private int gmlevel;
private Set<String> macs = new HashSet<>();
private Map<String, ScriptEngine> engines = new HashMap<>();
private Map<String, NashornScriptEngine> engines = new HashMap<>();
private byte characterSlots = 3;
private byte loginattempt = 0;
private String pin = "";
@@ -120,19 +117,31 @@ public class MapleClient {
private final Semaphore actionsSemaphore = new Semaphore(7);
private final Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT, true);
private final Lock encoderLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ENCODER, true);
private static final Lock loginLocks[] = new Lock[200]; // thanks Masterrulax & try2hack for pointing out a bottleneck issue here
private final Lock announcerLock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_ANNOUNCER, true);
private static final int lockCount = 200;
private static final Lock loginLocks[] = new Lock[lockCount]; // thanks Masterrulax & try2hack for pointing out a bottleneck issue here
private Calendar tempBanCalendar;
private int votePoints;
private int voteTime = -1;
private int visibleWorlds;
private long lastNpcClick;
private long sessionId;
private long lastPacket = System.currentTimeMillis();
private int lang = 0;
static {
for (int i = 0; i < 200; i++) {
for (int i = 0; i < lockCount; i++) {
loginLocks[i] = MonitoredReentrantLockFactory.createLock(MonitoredLockType.CLIENT_LOGIN, true);
}
}
public void updateLastPacket() {
lastPacket = System.currentTimeMillis();
}
public long getLastPacket() {
return lastPacket;
}
public MapleClient(MapleAESOFB send, MapleAESOFB receive, IoSession session) {
this.send = send;
@@ -444,14 +453,14 @@ public class MapleClient {
}
public int finishLogin() {
Lock loginLock = loginLocks[this.getAccID() % 200];
Lock loginLock = loginLocks[this.getAccID() % lockCount];
loginLock.lock();
try {
if (getLoginState() > LOGIN_NOTLOGGEDIN) { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN
loggedIn = false;
return 7;
}
updateLoginState(LOGIN_LOGGEDIN);
updateLoginState(MapleClient.LOGIN_LOGGEDIN);
} finally {
loginLock.unlock();
}
@@ -480,7 +489,7 @@ public class MapleClient {
}
public boolean checkPin(String other) {
if (!(ServerConstants.ENABLE_PIN && !canBypassPin())) {
if (!(YamlConfig.config.server.ENABLE_PIN && !canBypassPin())) {
return true;
}
@@ -517,7 +526,7 @@ public class MapleClient {
}
public boolean checkPic(String other) {
if (!(ServerConstants.ENABLE_PIC && !canBypassPic())) {
if (!(YamlConfig.config.server.ENABLE_PIC && !canBypassPic())) {
return true;
}
@@ -583,7 +592,7 @@ public class MapleClient {
loginok = (tos == 0) ? 23 : 0;
} else if (pwd.equals(passhash) || checkHash(passhash, "SHA-1", pwd) || checkHash(passhash, "SHA-512", pwd)) {
// thanks GabrielSin for detecting some no-bcrypt inconsistencies here
loginok = (tos == 0) ? (!ServerConstants.BCRYPT_MIGRATION ? 23 : -23) : (!ServerConstants.BCRYPT_MIGRATION ? 0 : -10); // migrate to bcrypt
loginok = (tos == 0) ? (!YamlConfig.config.server.BCRYPT_MIGRATION ? 23 : -23) : (!YamlConfig.config.server.BCRYPT_MIGRATION ? 0 : -10); // migrate to bcrypt
} else {
loggedIn = false;
loginok = 4;
@@ -640,7 +649,7 @@ public class MapleClient {
}
}
public Calendar getTempBanCalendar() {
public Calendar getTempBanCalendarFromDB() {
Connection con = null;
PreparedStatement ps = null;
ResultSet rs = null;
@@ -654,10 +663,12 @@ public class MapleClient {
return null;
}
long blubb = rs.getLong("tempban");
if (blubb == 0) { // basically if timestamp in db is 0000-00-00
if (blubb == 0 || rs.getString("tempban").equals("2018-06-20 00:00:00.0")) { // 0000-00-00 or 2018-06-20 (default set in LoginPasswordHandler)
return null;
}
lTempban.setTimeInMillis(rs.getTimestamp("tempban").getTime());
tempBanCalendar = lTempban;
return lTempban;
} catch (SQLException e) {
e.printStackTrace();
@@ -678,6 +689,14 @@ public class MapleClient {
}
return null;//why oh why!?!
}
public Calendar getTempBanCalendar() {
return tempBanCalendar;
}
public boolean hasBeenBanned() {
return tempBanCalendar != null;
}
public static long dottedQuadToLong(String dottedQuad) throws RuntimeException {
String[] quads = dottedQuad.split("\\.");
@@ -831,7 +850,7 @@ public class MapleClient {
if (rs.getTimestamp("lastlogin").getTime() + 30000 < Server.getInstance().getCurrentTime()) {
int accountId = accId;
state = LOGIN_NOTLOGGEDIN;
updateLoginState(LOGIN_NOTLOGGEDIN); // ACCID = 0, issue found thanks to Tochi & K u ssss o & Thora & Omo Oppa
updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN); // ACCID = 0, issue found thanks to Tochi & K u ssss o & Thora & Omo Oppa
this.setAccID(accountId);
}
}
@@ -865,9 +884,9 @@ public class MapleClient {
MapleMap map = player.getMap();
final MapleParty party = player.getParty();
final int idz = player.getId();
final MaplePartyCharacter chrp = new MaplePartyCharacter(player);
if (party != null) {
final MaplePartyCharacter chrp = new MaplePartyCharacter(player);
chrp.setOnline(false);
wserv.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp);
if (party.getLeader().getId() == idz && map != null) {
@@ -977,14 +996,9 @@ public class MapleClient {
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);
}
}
player.forfeitExpirableQuests(); //This is for those quests that you have to stay logged in for a certain amount of time
if (guild != null) {
final Server server = Server.getInstance();
server.setGuildMemberOnline(player, false, player.getClient().getChannel());
@@ -1017,6 +1031,7 @@ public class MapleClient {
player.saveCharToDB(true);
player.logOff();
if(YamlConfig.config.server.INSTANT_NAME_CHANGE) player.doPendingNameChange();
clear();
} else {
getChannelServer().removePlayer(player);
@@ -1038,7 +1053,8 @@ public class MapleClient {
MapleSessionCoordinator.getInstance().closeSession(session, false);
session.removeAttribute(MapleClient.CLIENT_KEY);
}
if (!Server.getInstance().hasCharacteridInTransition(session)) {
if (!Server.getInstance().hasCharacteridInTransition(this)) {
updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN);
}
@@ -1064,6 +1080,12 @@ public class MapleClient {
this.send = null;
//this.session = null;
}
public void setCharacterOnSessionTransitionState(int cid) {
this.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
session.setAttribute(MapleClient.CLIENT_TRANSITION);
Server.getInstance().setCharacteridInTransition(this, cid);
}
public int getChannel() {
return channel;
@@ -1162,11 +1184,11 @@ public class MapleClient {
gmlevel = level;
}
public void setScriptEngine(String name, ScriptEngine e) {
public void setScriptEngine(String name, NashornScriptEngine e) {
engines.put(name, e);
}
public ScriptEngine getScriptEngine(String name) {
public NashornScriptEngine getScriptEngine(String name) {
return engines.get(name);
}
@@ -1210,6 +1232,22 @@ public class MapleClient {
}
return disconnectForBeingAFaggot;
}
public void checkChar(int accid) { /// issue with multiple chars from same account login found by shavit, resinate
if (!YamlConfig.config.server.USE_CHARACTER_ACCOUNT_CHECK) {
return;
}
for (World w : Server.getInstance().getWorlds()) {
for (MapleCharacter chr : w.getPlayerStorage().getAllCharacters()) {
if (accid == chr.getAccountID()) {
FilePrinter.print(FilePrinter.EXPLOITS, "Player: " + chr.getName() + " has been removed from " + GameConstants.WORLD_NAMES[w.getId()] + ". Possible Dupe attempt.");
chr.getClient().forceDisconnect();
w.getPlayerStorage().removePlayer(chr.getId());
}
}
}
}
public int getVotePoints(){
int points = 0;
@@ -1285,12 +1323,18 @@ public class MapleClient {
actionsSemaphore.release();
}
public void lockEncoder() {
encoderLock.lock();
public boolean tryacquireEncoder() {
if (actionsSemaphore.tryAcquire()) {
encoderLock.lock();
return true;
} else {
return false;
}
}
public void unlockEncoder() {
encoderLock.unlock();
actionsSemaphore.release();
}
private static class CharNameAndId {
@@ -1323,6 +1367,10 @@ public class MapleClient {
return (short) Math.max(0, characterSlots - Server.getInstance().getAccountWorldCharacterCount(accId, world));
}
public short getAvailableCharacterWorldSlots(int world) {
return (short) Math.max(0, characterSlots - Server.getInstance().getAccountWorldCharacterCount(accId, world));
}
public short getCharacterSlots() {
return characterSlots;
}
@@ -1436,8 +1484,13 @@ public class MapleClient {
}
}
public synchronized void announce(final byte[] packet) {//MINA CORE IS A FUCKING BITCH AND I HATE IT <3
session.write(packet);
public void announce(final byte[] packet) { // thanks GitGud for noticing an opportunity for improvement by overcoming "synchronized announce"
announcerLock.lock();
try {
session.write(packet);
} finally {
announcerLock.unlock();
}
}
public void announceHint(String msg, int length) {
@@ -1492,8 +1545,7 @@ public class MapleClient {
player.saveCharToDB();
player.getClient().updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
player.setSessionTransitionState();
player.setSessionTransitionState();
try {
announce(MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1])));
} catch (IOException e) {

View File

@@ -21,7 +21,7 @@
*/
package client;
import constants.GameConstants;
import constants.game.GameConstants;
public enum MapleDisease {
NULL(0x0),

View File

@@ -25,75 +25,279 @@ import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import net.server.Server;
import net.server.world.World;
import tools.DatabaseConnection;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.Pair;
/**
*
* @author Jay Estrella - Mr.Trash :3
* @author Ubaware
*/
public class MapleFamily {
private static int id;
private static Map<Integer, MapleFamilyEntry> members = new HashMap<Integer, MapleFamilyEntry>();
public MapleFamily(int cid) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT familyid FROM family_character WHERE cid = ?");
ps.setInt(1, cid);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
id = rs.getInt("familyid");
}
ps.close();
rs.close();
con.close();
getMapleFamily();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
private static final AtomicInteger familyIDCounter = new AtomicInteger();
private static void getMapleFamily() {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT * FROM family_character WHERE familyid = ?");
ps.setInt(1, id);
ResultSet rs = ps.executeQuery();
while (rs.next()) {
MapleFamilyEntry ret = new MapleFamilyEntry();
ret.setFamilyId(id);
ret.setRank(rs.getInt("rank"));
ret.setReputation(rs.getInt("reputation"));
ret.setTotalJuniors(rs.getInt("totaljuniors"));
ret.setFamilyName(rs.getString("name"));
ret.setJuniors(rs.getInt("juniorsadded"));
ret.setTodaysRep(rs.getInt("todaysrep"));
int cid = rs.getInt("cid");
ret.setChrId(cid);
members.put(cid, ret);
}
rs.close();
ps.close();
con.close();
} catch (SQLException sqle) {
sqle.printStackTrace();
}
}
private final int id, world;
private final Map<Integer, MapleFamilyEntry> members = new ConcurrentHashMap<Integer, MapleFamilyEntry>();
private MapleFamilyEntry leader;
private String name;
private String preceptsMessage = "";
private int totalGenerations;
public MapleFamilyEntry getMember(int cid) {
if (members.containsKey(cid)){
return members.get(cid);
}
return null;
}
public Map<Integer, MapleFamilyEntry> getMembers() {
return members;
}
public void broadcast(byte[] packet) {
// family currently not developed
public MapleFamily(int id, int world) {
int newId = id;
if(id == -1) {
// get next available family id
while(idInUse(newId = familyIDCounter.incrementAndGet())) {
}
}
this.id = newId;
this.world = world;
}
private static boolean idInUse(int id) {
for(World world : Server.getInstance().getWorlds()) {
if(world.getFamily(id) != null) return true;
}
return false;
}
public int getID() {
return id;
}
public int getWorld() {
return world;
}
public void setLeader(MapleFamilyEntry leader) {
this.leader = leader;
setName(leader.getName());
}
public MapleFamilyEntry getLeader() {
return leader;
}
private void setName(String name) {
this.name = name;
}
public int getTotalMembers() {
return members.size();
}
public int getTotalGenerations() {
return totalGenerations;
}
public void setTotalGenerations(int generations) {
this.totalGenerations = generations;
}
public String getName() {
return this.name;
}
public void setMessage(String message, boolean save) {
this.preceptsMessage = message;
if(save) {
try (Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("UPDATE family_character SET precepts = ? WHERE cid = ?")) {
ps.setString(1, message);
ps.setInt(2, getLeader().getChrId());
ps.executeUpdate();
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not save new precepts for family " + getID() + ".");
e.printStackTrace();
}
}
}
public String getMessage() {
return preceptsMessage;
}
public void addEntry(MapleFamilyEntry entry) {
members.put(entry.getChrId(), entry);
}
public void removeEntryBranch(MapleFamilyEntry root) {
members.remove(root.getChrId());
for(MapleFamilyEntry junior : root.getJuniors()) {
if(junior != null) removeEntryBranch(junior);
}
}
public void addEntryTree(MapleFamilyEntry root) {
members.put(root.getChrId(), root);
for(MapleFamilyEntry junior : root.getJuniors()) {
if(junior != null) addEntryTree(junior);
}
}
public MapleFamilyEntry getEntryByID(int cid) {
return members.get(cid);
}
public void broadcast(byte[] packet) {
broadcast(packet, -1);
}
public void broadcast(byte[] packet, int ignoreID) {
for(MapleFamilyEntry entry : members.values()) {
MapleCharacter chr = entry.getChr();
if(chr != null) {
if(chr.getId() == ignoreID) continue;
chr.getClient().announce(packet);
}
}
}
public void broadcastFamilyInfoUpdate() {
for(MapleFamilyEntry entry : members.values()) {
MapleCharacter chr = entry.getChr();
if(chr != null) {
chr.getClient().announce(MaplePacketCreator.getFamilyInfo(entry));
}
}
}
public void resetDailyReps() {
for(MapleFamilyEntry entry : members.values()) {
entry.setTodaysRep(0);
entry.setRepsToSenior(0);
entry.resetEntitlementUsages();
}
}
public static void loadAllFamilies() {
try(Connection con = DatabaseConnection.getConnection()) {
List<Pair<Pair<Integer, Integer>, MapleFamilyEntry>> unmatchedJuniors = new ArrayList<Pair<Pair<Integer, Integer>, MapleFamilyEntry>>(200); // <<world, seniorid> familyEntry>
try(PreparedStatement psEntries = con.prepareStatement("SELECT * FROM family_character")) {
ResultSet rsEntries = psEntries.executeQuery();
while(rsEntries.next()) { // can be optimized
int cid = rsEntries.getInt("cid");
String name = null;
int level = -1;
int jobID = -1;
int world = -1;
try(PreparedStatement ps = con.prepareStatement("SELECT world, name, level, job FROM characters WHERE id = ?")) {
ps.setInt(1, cid);
ResultSet rs = ps.executeQuery();
if(rs.next()) {
world = rs.getInt("world");
name = rs.getString("name");
level = rs.getInt("level");
jobID = rs.getInt("job");
} else {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Could not load character information of " + cid + " in loadAllFamilies(). (RECORD DOES NOT EXIST)");
continue;
}
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not load character information of " + cid + " in loadAllFamilies(). (SQL ERROR)");
continue;
}
int familyid = rsEntries.getInt("familyid");
int seniorid = rsEntries.getInt("seniorid");
int reputation = rsEntries.getInt("reputation");
int todaysRep = rsEntries.getInt("todaysrep");
int totalRep = rsEntries.getInt("totalreputation");
int repsToSenior = rsEntries.getInt("reptosenior");
String precepts = rsEntries.getString("precepts");
//Timestamp lastResetTime = rsEntries.getTimestamp("lastresettime"); //taken care of by FamilyDailyResetTask
World wserv = Server.getInstance().getWorld(world);
if (wserv == null) {
continue;
}
MapleFamily family = wserv.getFamily(familyid);
if(family == null) {
family = new MapleFamily(familyid, world);
Server.getInstance().getWorld(world).addFamily(familyid, family);
}
MapleFamilyEntry familyEntry = new MapleFamilyEntry(family, cid, name, level, MapleJob.getById(jobID));
family.addEntry(familyEntry);
if(seniorid <= 0) {
family.setLeader(familyEntry);
family.setMessage(precepts, false);
}
MapleFamilyEntry senior = family.getEntryByID(seniorid);
if(senior != null) {
familyEntry.setSenior(family.getEntryByID(seniorid), false);
} else {
if(seniorid > 0) unmatchedJuniors.add(new Pair<Pair<Integer, Integer>, MapleFamilyEntry>(new Pair<Integer, Integer>(world, seniorid), familyEntry));
}
familyEntry.setReputation(reputation);
familyEntry.setTodaysRep(todaysRep);
familyEntry.setTotalReputation(totalRep);
familyEntry.setRepsToSenior(repsToSenior);
//load used entitlements
try (PreparedStatement ps = con.prepareStatement("SELECT entitlementid FROM family_entitlement WHERE charid = ?")) {
ps.setInt(1, familyEntry.getChrId());
ResultSet rs = ps.executeQuery();
while(rs.next()) {
familyEntry.setEntitlementUsed(rs.getInt("entitlementid"));
}
}
}
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get family_character entries.");
e.printStackTrace();
}
// link missing ones (out of order)
for(Pair<Pair<Integer, Integer>, MapleFamilyEntry> unmatchedJunior : unmatchedJuniors) {
int world = unmatchedJunior.getLeft().getLeft();
int seniorid = unmatchedJunior.getLeft().getRight();
MapleFamilyEntry junior = unmatchedJunior.getRight();
MapleFamilyEntry senior = Server.getInstance().getWorld(world).getFamily(junior.getFamily().getID()).getEntryByID(seniorid);
if(senior != null) {
junior.setSenior(senior, false);
} else {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Missing senior for character " + junior.getName() + " in world " + world);
}
}
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get DB connection.");
e.printStackTrace();
}
for(World world : Server.getInstance().getWorlds()) {
for(MapleFamily family : world.getFamilies()) {
family.getLeader().doFullCount();
}
}
}
public void saveAllMembersRep() { //was used for autosave task, but character autosave should be enough
try(Connection con = DatabaseConnection.getConnection()) {
con.setAutoCommit(false);
boolean success = true;
for(MapleFamilyEntry entry : members.values()) {
success = entry.saveReputation(con);
if(!success) break;
}
if(!success) {
con.rollback();
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Family rep autosave failed for family " + getID() + " on " + Calendar.getInstance().getTime().toString() + ".");
}
con.setAutoCommit(true);
//reset repChanged after successful save
for(MapleFamilyEntry entry : members.values()) {
entry.savedSuccessfully();
}
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB.");
e.printStackTrace();
}
}
}

View File

@@ -0,0 +1,41 @@
package client;
public enum MapleFamilyEntitlement {
FAMILY_REUINION(1, 300, "Family Reunion", "[Target] Me\\n[Effect] Teleport directly to the Family member of your choice."),
SUMMON_FAMILY(1, 500, "Summon Family", "[Target] 1 Family member\\n[Effect] Summon a Family member of choice to the map you're in."),
SELF_DROP_1_5(1, 700, "My Drop Rate 1.5x (15 min)", "[Target] Me\\n[Time] 15 min.\\n[Effect] Monster drop rate will be increased #c1.5x#.\\n* If the Drop Rate event is in progress, this will be nullified."),
SELF_EXP_1_5(1, 800, "My EXP 1.5x (15 min)", "[Target] Me\\n[Time] 15 min.\\n[Effect] EXP earned from hunting will be increased #c1.5x#.\\n* If the EXP event is in progress, this will be nullified."),
FAMILY_BONDING(1, 1000, "Family Bonding (30 min)", "[Target] At least 6 Family members online that are below me in the Pedigree\\n[Time] 30 min.\\n[Effect] Monster drop rate and EXP earned will be increased #c2x#. \\n* If the EXP event is in progress, this will be nullified."),
SELF_DROP_2(1, 1200, "My Drop Rate 2x (15 min)", "[Target] Me\\n[Time] 15 min.\\n[Effect] Monster drop rate will be increased #c2x#.\\n* If the Drop Rate event is in progress, this will be nullified."),
SELF_EXP_2(1, 1500, "My EXP 2x (15 min)", "[Target] Me\\n[Time] 15 min.\\n[Effect] EXP earned from hunting will be increased #c2x#.\\n* If the EXP event is in progress, this will be nullified."),
SELF_DROP_2_30MIN(1, 2000, "My Drop Rate 2x (30 min)", "[Target] Me\\n[Time] 30 min.\\n[Effect] Monster drop rate will be increased #c2x#.\\n* If the Drop Rate event is in progress, this will be nullified."),
SELF_EXP_2_30MIN(1, 2500, "My EXP 2x (30 min)", "[Target] Me\\n[Time] 30 min.\\n[Effect] EXP earned from hunting will be increased #c2x#. \\n* If the EXP event is in progress, this will be nullified."),
PARTY_DROP_2_30MIN(1, 4000, "My Party Drop Rate 2x (30 min)", "[Target] My party\\n[Time] 30 min.\\n[Effect] Monster drop rate will be increased #c2x#.\\n* If the Drop Rate event is in progress, this will be nullified."),
PARTY_EXP_2_30MIN(1, 5000, "My Party EXP 2x (30 min)", "[Target] My party\\n[Time] 30 min.\\n[Effect] EXP earned from hunting will be increased #c2x#.\\n* If the EXP event is in progress, this will be nullified.");
private final int usageLimit, repCost;
private final String name, description;
private MapleFamilyEntitlement(int usageLimit, int repCost, String name, String description) {
this.usageLimit = usageLimit;
this.repCost = repCost;
this.name = name;
this.description = description;
}
public int getUsageLimit() {
return usageLimit;
}
public int getRepCost() {
return repCost;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
}

View File

@@ -1,8 +1,6 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This 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
@@ -21,33 +19,210 @@
*/
package client;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import net.server.Server;
import tools.DatabaseConnection;
import tools.FilePrinter;
import tools.MaplePacketCreator;
import tools.Pair;
/**
* @author Ubaware
*/
public class MapleFamilyEntry {
private int familyId;
private int rank, reputation, totalReputation, todaysRep, totalJuniors, juniors, chrid;
private String familyName;
private final int characterID;
private volatile MapleFamily family;
private volatile MapleCharacter character;
public int getId() {
return familyId;
private volatile MapleFamilyEntry senior;
private final MapleFamilyEntry[] juniors = new MapleFamilyEntry[2];
private final int[] entitlements = new int[11];
private volatile int reputation, totalReputation;
private volatile int todaysRep, repsToSenior; //both are daily values
private volatile int totalJuniors, totalSeniors;
private volatile int generation;
private volatile boolean repChanged; //used to ignore saving unchanged rep values
// cached values for offline players
private String charName;
private int level;
private MapleJob job;
public MapleFamilyEntry(MapleFamily family, int characterID, String charName, int level, MapleJob job) {
this.family = family;
this.characterID = characterID;
this.charName = charName;
this.level = level;
this.job = job;
}
public void setFamilyId(int familyId) {
this.familyId = familyId;
public MapleCharacter getChr() {
return character;
}
public int getRank() {
return rank;
public void setCharacter(MapleCharacter newCharacter) {
if(newCharacter == null) cacheOffline(newCharacter);
else newCharacter.setFamilyEntry(this);
this.character = newCharacter;
}
public void setRank(int rank) {
this.rank = rank;
private void cacheOffline(MapleCharacter chr) {
if(chr != null) {
charName = chr.getName();
level = chr.getLevel();
job = chr.getJob();
}
}
public synchronized void join(MapleFamilyEntry senior) {
if(senior == null || getSenior() != null) return;
MapleFamily oldFamily = getFamily();
MapleFamily newFamily = senior.getFamily();
setSenior(senior, false);
addSeniorCount(newFamily.getTotalGenerations(), newFamily); //count will be overwritten by doFullCount()
newFamily.getLeader().doFullCount(); //easier than keeping track of numbers
oldFamily.setMessage(null, true);
newFamily.addEntryTree(this);
Server.getInstance().getWorld(oldFamily.getWorld()).removeFamily(oldFamily.getID());
//db
try(Connection con = DatabaseConnection.getConnection()) {
con.setAutoCommit(false);
boolean success = updateDBChangeFamily(con, getChrId(), newFamily.getID(), senior.getChrId());
for(MapleFamilyEntry junior : juniors) { // better to duplicate this than the SQL code
if(junior != null) {
success = junior.updateNewFamilyDB(con); // recursively updates juniors in db
if(!success) break;
}
}
if(!success) {
con.rollback();
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Could not absorb " + oldFamily.getName() + " family into " + newFamily.getName() + " family. (SQL ERROR)");
}
con.setAutoCommit(true);
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB.");
e.printStackTrace();
}
}
public synchronized void fork() {
MapleFamily oldFamily = getFamily();
MapleFamilyEntry oldSenior = getSenior();
family = new MapleFamily(-1, oldFamily.getWorld());
Server.getInstance().getWorld(family.getWorld()).addFamily(family.getID(), family);
setSenior(null, false);
family.setLeader(this);
addSeniorCount(-getTotalSeniors(), family);
setTotalSeniors(0);
if(oldSenior != null) {
oldSenior.addJuniorCount(-getTotalJuniors());
oldSenior.removeJunior(this);
oldFamily.getLeader().doFullCount();
}
oldFamily.removeEntryBranch(this);
family.addEntryTree(this);
this.repsToSenior = 0;
this.repChanged = true;
family.setMessage("", true);
doFullCount(); //to make sure all counts are correct
// update db
try(Connection con = DatabaseConnection.getConnection()) {
con.setAutoCommit(false);
boolean success = updateDBChangeFamily(con, getChrId(), getFamily().getID(), 0);
for(MapleFamilyEntry junior : juniors) { // better to duplicate this than the SQL code
if(junior != null) {
success = junior.updateNewFamilyDB(con); // recursively updates juniors in db
if(!success) break;
}
}
if(!success) {
con.rollback();
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Could not fork family with new leader " + getName() + ". (Old senior : " + oldSenior.getName() + ", leader :" + oldFamily.getLeader().getName() + ")");
}
con.setAutoCommit(true);
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB.");
e.printStackTrace();
}
}
private synchronized boolean updateNewFamilyDB(Connection con) {
if(!updateFamilyEntryDB(con, getChrId(), getFamily().getID())) return false;
if(!updateCharacterFamilyDB(con, getChrId(), getFamily().getID(), true)) return false;
for(MapleFamilyEntry junior : juniors) {
if(junior != null) {
if(!junior.updateNewFamilyDB(con)) return false;
}
}
return true;
}
private static boolean updateFamilyEntryDB(Connection con, int cid, int familyid) {
try(PreparedStatement ps = con.prepareStatement("UPDATE family_character SET familyid = ? WHERE cid = ?")) {
ps.setInt(1, familyid);
ps.setInt(2, cid);
ps.executeUpdate();
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not update family id in 'family_character' for character id " + cid + ". (fork)");
e.printStackTrace();
return false;
}
return true;
}
private synchronized void addSeniorCount(int seniorCount, MapleFamily newFamily) { // traverses tree and subtracts seniors and updates family
if(newFamily != null) this.family = newFamily;
setTotalSeniors(getTotalSeniors() + seniorCount);
this.generation += seniorCount;
for(MapleFamilyEntry junior : juniors) {
if(junior != null) junior.addSeniorCount(seniorCount, newFamily);
}
}
private synchronized void addJuniorCount(int juniorCount) { // climbs tree and adds junior count
setTotalJuniors(getTotalJuniors() + juniorCount);
MapleFamilyEntry senior = getSenior();
if(senior != null) senior.addJuniorCount(juniorCount);
}
public MapleFamily getFamily() {
return family;
}
public int getChrId() {
return chrid;
return characterID;
}
public void setChrId(int chrid) {
this.chrid = chrid;
public String getName() {
MapleCharacter chr = character;
if(chr != null) return chr.getName();
else return charName;
}
public int getLevel() {
MapleCharacter chr = character;
if(chr != null) return chr.getLevel();
else return level;
}
public MapleJob getJob() {
MapleCharacter chr = character;
if(chr != null) return chr.getJob();
else return job;
}
public int getReputation() {
@@ -59,16 +234,182 @@ public class MapleFamilyEntry {
}
public void setReputation(int reputation) {
if(reputation != this.reputation) this.repChanged = true;
this.reputation = reputation;
}
public void setTodaysRep(int today) {
if(today != todaysRep) this.repChanged = true;
this.todaysRep = today;
}
public void gainReputation(int gain) {
public int getRepsToSenior() {
return repsToSenior;
}
public void setRepsToSenior(int reputation) {
if(reputation != this.repsToSenior) this.repChanged = true;
this.repsToSenior = reputation;
}
public void gainReputation(int gain, boolean countTowardsTotal) {
gainReputation(gain, countTowardsTotal, this);
}
private void gainReputation(int gain, boolean countTowardsTotal, MapleFamilyEntry from) {
if(gain != 0) repChanged = true;
this.reputation += gain;
this.totalReputation += gain;
this.todaysRep += gain;
if(gain > 0 && countTowardsTotal) {
this.totalReputation += gain;
}
MapleCharacter chr = getChr();
if(chr != null) chr.announce(MaplePacketCreator.sendGainRep(gain, from != null ? from.getName() : ""));
}
public void giveReputationToSenior(int gain, boolean includeSuperSenior) {
int actualGain = gain;
MapleFamilyEntry senior = getSenior();
if(senior != null && senior.getLevel() < getLevel() && gain > 0) actualGain /= 2; //don't halve negative values
if(senior != null) {
senior.gainReputation(actualGain, true, this);
if(actualGain > 0) {
this.repsToSenior += actualGain;
this.repChanged = true;
}
if(includeSuperSenior) {
senior = senior.getSenior();
if(senior != null) {
senior.gainReputation(actualGain, true, this);
}
}
}
}
public int getTotalReputation() {
return totalReputation;
}
public void setTotalReputation(int totalReputation) {
if(totalReputation != this.totalReputation) this.repChanged = true;
this.totalReputation = totalReputation;
}
public MapleFamilyEntry getSenior() {
return senior;
}
public synchronized boolean setSenior(MapleFamilyEntry senior, boolean save) {
if(this.senior == senior) return false;
MapleFamilyEntry oldSenior = this.senior;
this.senior = senior;
if(senior != null) {
if(senior.addJunior(this)) {
if(save) {
updateDBChangeFamily(getChrId(), senior.getFamily().getID(), senior.getChrId());
}
if(this.repsToSenior != 0) this.repChanged = true;
this.repsToSenior = 0;
this.addSeniorCount(1, null);
this.setTotalSeniors(senior.getTotalSeniors() + 1);
return true;
}
} else {
if(oldSenior != null) {
oldSenior.removeJunior(this);
}
}
return false;
}
private static boolean updateDBChangeFamily(int cid, int familyid, int seniorid) {
try(Connection con = DatabaseConnection.getConnection()) {
return updateDBChangeFamily(con, cid, familyid, seniorid);
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB.");
e.printStackTrace();
return false;
}
}
private static boolean updateDBChangeFamily(Connection con, int cid, int familyid, int seniorid) {
try(PreparedStatement ps = con.prepareStatement("UPDATE family_character SET familyid = ?, seniorid = ?, reptosenior = 0 WHERE cid = ?")) {
ps.setInt(1, familyid);
ps.setInt(2, seniorid);
ps.setInt(3, cid);
ps.executeUpdate();
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not update seniorid in 'family_character' for character id " + cid + ".");
e.printStackTrace();
return false;
}
return updateCharacterFamilyDB(con, cid, familyid, false);
}
private static boolean updateCharacterFamilyDB(Connection con, int charid, int familyid, boolean fork) {
try(PreparedStatement ps = con.prepareStatement("UPDATE characters SET familyid = ? WHERE id = ?")) {
ps.setInt(1, familyid);
ps.setInt(2, charid);
ps.executeUpdate();
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not update familyid in 'characters' for character id " + charid + " when changing family. " + (fork ? "(fork)" : ""));
e.printStackTrace();
return false;
}
return true;
}
public List<MapleFamilyEntry> getJuniors() {
return Collections.unmodifiableList(Arrays.asList(juniors));
}
public MapleFamilyEntry getOtherJunior(MapleFamilyEntry junior) {
if(juniors[0] == junior) return juniors[1];
else if(juniors[1] == junior) return juniors[0];
return null;
}
public int getJuniorCount() { //close enough to be relatively consistent to multiple threads (and the result is not vital)
int juniorCount = 0;
if(juniors[0] != null) juniorCount++;
if(juniors[1] != null) juniorCount++;
return juniorCount;
}
public synchronized boolean addJunior(MapleFamilyEntry newJunior) {
for(int i = 0; i < juniors.length; i++) {
if(juniors[i] == null) { // successfully add new junior to family
juniors[i] = newJunior;
addJuniorCount(1);
getFamily().addEntry(newJunior);
return true;
}
}
return false;
}
public synchronized boolean isJunior(MapleFamilyEntry entry) { //require locking since result accuracy is vital
if(juniors[0] == entry) return true;
else if(juniors[1] == entry) return true;
return false;
}
public synchronized boolean removeJunior(MapleFamilyEntry junior) {
for(int i = 0; i < juniors.length; i++) {
if(juniors[i] == junior) {
juniors[i] = null;
return true;
}
}
return false;
}
public int getTotalSeniors() {
return totalSeniors;
}
public void setTotalSeniors(int totalSeniors) {
this.totalSeniors = totalSeniors;
}
public int getTotalJuniors() {
@@ -78,28 +419,134 @@ public class MapleFamilyEntry {
public void setTotalJuniors(int totalJuniors) {
this.totalJuniors = totalJuniors;
}
public int getJuniors() {
return juniors;
public void announceToSenior(byte[] packet, boolean includeSuperSenior) {
MapleFamilyEntry senior = getSenior();
if(senior != null) {
MapleCharacter seniorChr = senior.getChr();
if(seniorChr != null) seniorChr.announce(packet);
senior = senior.getSenior();
if(includeSuperSenior && senior != null) {
seniorChr = senior.getChr();
if(seniorChr != null) seniorChr.announce(packet);
}
}
}
public void updateSeniorFamilyInfo(boolean includeSuperSenior) {
MapleFamilyEntry senior = getSenior();
if(senior != null) {
MapleCharacter seniorChr = senior.getChr();
if(seniorChr != null) seniorChr.announce(MaplePacketCreator.getFamilyInfo(senior));
senior = senior.getSenior();
if(includeSuperSenior && senior != null) {
seniorChr = senior.getChr();
if(seniorChr != null) seniorChr.announce(MaplePacketCreator.getFamilyInfo(senior));
}
}
}
public void setJuniors(int juniors) {
this.juniors = juniors;
/**
* Traverses entire family tree to update senior/junior counts. Call on leader.
*/
public synchronized void doFullCount() {
Pair<Integer, Integer> counts = this.traverseAndUpdateCounts(0);
getFamily().setTotalGenerations(counts.getLeft() + 1);
}
public void setFamilyName(String familyName) {
this.familyName = familyName;
private Pair<Integer, Integer> traverseAndUpdateCounts(int seniors) { // recursion probably limits family size, but it should handle a depth of a few thousand
setTotalSeniors(seniors);
this.generation = seniors;
int juniorCount = 0;
int highestGeneration = this.generation;
for(MapleFamilyEntry entry : juniors) {
if(entry != null) {
Pair<Integer, Integer> counts = entry.traverseAndUpdateCounts(seniors + 1);
juniorCount += counts.getRight(); //total juniors
if(counts.getLeft() > highestGeneration) highestGeneration = counts.getLeft();
}
}
setTotalJuniors(juniorCount);
return new Pair<>(highestGeneration, juniorCount); //creating new objects to return is a bit inefficient, but cleaner than packing into a long
}
public String getFamilyName() {
return familyName;
public boolean useEntitlement(MapleFamilyEntitlement entitlement) {
int id = entitlement.ordinal();
if(entitlements[id] >= 1) return false;
try(Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("INSERT INTO family_entitlement (entitlementid, charid, timestamp) VALUES (?, ?, ?)")) {
ps.setInt(1, id);
ps.setInt(2, getChrId());
ps.setLong(3, System.currentTimeMillis());
ps.executeUpdate();
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not insert new row in 'family_entitlement' for character " + getName() + ".");
e.printStackTrace();
}
entitlements[id]++;
return true;
}
public boolean refundEntitlement(MapleFamilyEntitlement entitlement) {
int id = entitlement.ordinal();
try(Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("DELETE FROM family_entitlement WHERE entitlementid = ? AND charid = ?")) {
ps.setInt(1, id);
ps.setInt(2, getChrId());
ps.executeUpdate();
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not refund family entitlement \"" + entitlement.getName() + "\" for character " + getName() + ".");
e.printStackTrace();
}
entitlements[id] = 0;
return true;
}
public int getTotalReputation() {
return totalReputation;
public boolean isEntitlementUsed(MapleFamilyEntitlement entitlement) {
return entitlements[entitlement.ordinal()] >= 1;
}
public void setTotalReputation(int totalReputation) {
this.totalReputation = totalReputation;
public int getEntitlementUsageCount(MapleFamilyEntitlement entitlement) {
return entitlements[entitlement.ordinal()];
}
public void setEntitlementUsed(int id) {
entitlements[id]++;
}
public void resetEntitlementUsages() {
for(MapleFamilyEntitlement entitlement : MapleFamilyEntitlement.values()) {
entitlements[entitlement.ordinal()] = 0;
}
}
public boolean saveReputation() {
if(!repChanged) return true;
try(Connection con = DatabaseConnection.getConnection()) {
return saveReputation(con);
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB.");
e.printStackTrace();
return false;
}
}
public boolean saveReputation(Connection con) {
if(!repChanged) return true;
try (PreparedStatement ps = con.prepareStatement("UPDATE family_character SET reputation = ?, todaysrep = ?, totalreputation = ?, reptosenior = ? WHERE cid = ?")) {
ps.setInt(1, getReputation());
ps.setInt(2, getTodaysRep());
ps.setInt(3, getTotalReputation());
ps.setInt(4, getRepsToSenior());
ps.setInt(5, getChrId());
ps.executeUpdate();
} catch(SQLException e) {
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Failed to autosave rep to 'family_character' for charid " + getChrId());
e.printStackTrace();
return false;
}
return true;
}
public void savedSuccessfully() {
this.repChanged = false;
}
}

View File

@@ -107,6 +107,10 @@ public class MapleMount {
public void setItemId(int newitemid) {
this.itemid = newitemid;
}
public void setSkillId(int newskillid) {
this.skillid = newskillid;
}
public void setActive(boolean set) {
this.active = set;

View File

@@ -151,14 +151,20 @@ public class MapleQuestStatus {
}
public boolean progress(int id) {
if (progress.get(id) != null) {
int current = Integer.parseInt(progress.get(id));
String str = StringUtil.getLeftPaddedStr(Integer.toString(current + 1), '0', 3);
progress.put(id, str);
//this.setUpdated();
return true;
String currentStr = progress.get(id);
if (currentStr == null) {
return false;
}
return false;
int current = Integer.parseInt(currentStr);
if (current >= this.getQuest().getMobAmountNeeded(id)) {
return false;
}
String str = StringUtil.getLeftPaddedStr(Integer.toString(++current), '0', 3);
progress.put(id, str);
//this.setUpdated();
return true;
}
public void setProgress(int id, String pr) {
@@ -169,15 +175,14 @@ public class MapleQuestStatus {
public boolean madeProgress() {
return progress.size() > 0;
}
public Integer getAnyProgressKey() {
if (!progress.isEmpty()) return progress.entrySet().iterator().next().getKey();
return 0;
}
public String getProgress(int id) {
if (progress.get(id) == null) return "";
return progress.get(id);
String ret = progress.get(id);
if (ret == null) {
return "";
} else {
return ret;
}
}
public void resetProgress(int id) {
@@ -193,6 +198,27 @@ public class MapleQuestStatus {
public Map<Integer, String> getProgress() {
return Collections.unmodifiableMap(progress);
}
public short getInfoNumber() {
MapleQuest q = this.getQuest();
Status s = this.getStatus();
return q.getInfoNumber(s);
}
public String getInfoEx(int index) {
MapleQuest q = this.getQuest();
Status s = this.getStatus();
return q.getInfoEx(s, index);
}
public List<String> getInfoEx() {
MapleQuest q = this.getQuest();
Status s = this.getStatus();
return q.getInfoEx(s);
}
public long getCompletionTime() {
return completionTime;
@@ -217,18 +243,6 @@ public class MapleQuestStatus {
public int getCompleted() {
return completed;
}
public String getInfo() {
if(!progress.containsKey(0) && !getMedalMaps().isEmpty()) {
return Integer.toString(getMedalProgress());
}
return getProgress(0);
}
public void setInfo(String newInfo) {
progress.put(0, newInfo);
//this.setUpdated();
}
public void setForfeited(int forfeited) {
if (forfeited >= this.forfeited) {
@@ -254,11 +268,11 @@ public class MapleQuestStatus {
return customData;
}
public String getQuestData() {
public String getProgressData() {
StringBuilder str = new StringBuilder();
for (String ps : progress.values()) {
str.append(ps);
}
return str.toString();
}
}
}

View File

@@ -29,6 +29,7 @@ import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.Semaphore;
@@ -46,10 +47,10 @@ public final class MonsterBook {
private Map<Integer, Integer> cards = new LinkedHashMap<>();
private Lock lock = MonitoredReentrantLockFactory.createLock(MonitoredLockType.BOOK);
private Set<Entry<Integer, Integer>> getCardSet() {
public Set<Entry<Integer, Integer>> getCardSet() {
lock.lock();
try {
return Collections.unmodifiableSet(cards.entrySet());
return new HashSet<>(cards.entrySet());
} finally {
lock.unlock();
}
@@ -82,7 +83,9 @@ public final class MonsterBook {
}
if(qty < 5) {
calculateLevel(); // current leveling system only accounts unique cards...
if (qty == 0) { // leveling system only accounts unique cards
calculateLevel();
}
c.announce(MaplePacketCreator.addCard(false, cardid, qty + 1));
c.announce(MaplePacketCreator.showGainCard());
@@ -94,7 +97,15 @@ public final class MonsterBook {
private void calculateLevel() {
lock.lock();
try {
bookLevel = (int) Math.max(1, Math.sqrt((normalCard + specialCard) / 5));
int collectionExp = (normalCard + specialCard);
int level = 0, expToNextlevel = 1;
do {
level++;
expToNextlevel += level * 10;
} while (collectionExp >= expToNextlevel);
bookLevel = level; // thanks IxianMace for noticing book level differing between book UI and character info UI
} finally {
lock.unlock();
}
@@ -232,4 +243,29 @@ public final class MonsterBook {
e.printStackTrace();
}
}
public static int[] getCardTierSize() {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM monstercarddata GROUP BY floor(cardid / 1000);");
ResultSet rs = ps.executeQuery();
rs.last();
int[] tierSizes = new int[rs.getRow()];
rs.beforeFirst();
while (rs.next()) {
tierSizes[rs.getRow() - 1] = rs.getInt(1);
}
rs.close();
ps.close();
con.close();
return tierSizes;
} catch (SQLException e) {
e.printStackTrace();
return new int[0];
}
}
}

View File

@@ -68,7 +68,7 @@ import constants.skills.Shadower;
import constants.skills.Sniper;
import constants.skills.Spearman;
import constants.skills.SuperGM;
import constants.skills.Swordsman;
import constants.skills.Warrior;
import constants.skills.ThunderBreaker;
import constants.skills.WhiteKnight;
import constants.skills.WindArcher;
@@ -180,6 +180,7 @@ public class SkillFactory {
case NightWalker.POISON_BOMB:
case NightWalker.VAMPIRE:
case ChiefBandit.CHAKRA:
case Aran.COMBAT_STEP:
case Evan.RECOVERY_AURA:
isBuff = false;
break;
@@ -188,7 +189,7 @@ public class SkillFactory {
case Beginner.MONSTER_RIDER:
case Beginner.ECHO_OF_HERO:
case Beginner.MAP_CHAIR:
case Swordsman.IRON_BODY:
case Warrior.IRON_BODY:
case Fighter.AXE_BOOSTER:
case Fighter.POWER_GUARD:
case Fighter.RAGE:

View File

@@ -49,6 +49,18 @@ public class SkillMacro {
public int getSkill3() {
return skill3;
}
public void setSkill1(int skill) {
skill1 = skill;
}
public void setSkill2(int skill) {
skill2 = skill;
}
public void setSkill3(int skill) {
skill3 = skill;
}
public String getName() {
return name;

View File

@@ -23,7 +23,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package client.autoban;
import client.MapleCharacter;
import constants.ServerConstants;
import config.YamlConfig;
import net.server.Server;
import tools.FilePrinter;
import tools.MapleLogger;
@@ -84,19 +84,19 @@ public enum AutobanFactory {
}
public void alert(MapleCharacter chr, String reason) {
if(ServerConstants.USE_AUTOBAN == true) {
if(YamlConfig.config.server.USE_AUTOBAN == true) {
if (chr != null && MapleLogger.ignored.contains(chr.getId())){
return;
}
Server.getInstance().broadcastGMMessage((chr != null ? chr.getWorld() : 0), MaplePacketCreator.sendYellowTip((chr != null ? MapleCharacter.makeMapleReadable(chr.getName()) : "") + " caused " + this.name() + " " + reason));
}
if (ServerConstants.USE_AUTOBAN_LOG) {
if (YamlConfig.config.server.USE_AUTOBAN_LOG) {
FilePrinter.print(FilePrinter.AUTOBAN_WARNING, (chr != null ? MapleCharacter.makeMapleReadable(chr.getName()) : "") + " caused " + this.name() + " " + reason);
}
}
public void autoban(MapleCharacter chr, String value) {
if(ServerConstants.USE_AUTOBAN == true) {
if(YamlConfig.config.server.USE_AUTOBAN == true) {
chr.autoban("Autobanned for (" + this.name() + ": " + value + ")");
//chr.sendPolice("You will be disconnected for (" + this.name() + ": " + value + ")");
}

View File

@@ -6,7 +6,7 @@
package client.autoban;
import client.MapleCharacter;
import constants.ServerConstants;
import config.YamlConfig;
import java.util.HashMap;
import java.util.Map;
import net.server.Server;
@@ -33,10 +33,11 @@ public class AutobanManager {
}
public void addPoint(AutobanFactory fac, String reason) {
if (chr.isGM() || chr.isBanned()){
return;
}
if (ServerConstants.USE_AUTOBAN) {
if (YamlConfig.config.server.USE_AUTOBAN) {
if (chr.isGM() || chr.isBanned()){
return;
}
if (lastTime.containsKey(fac)) {
if (lastTime.get(fac) < (Server.getInstance().getCurrentTime() - fac.getExpire())) {
points.put(fac, points.get(fac) / 2); //So the points are not completely gone.
@@ -56,7 +57,7 @@ public class AutobanManager {
//chr.sendPolice("You have been blocked by #bMooplePolice for the HACK reason#k.");
}
}
if (ServerConstants.USE_AUTOBAN_LOG) {
if (YamlConfig.config.server.USE_AUTOBAN_LOG) {
// Lets log every single point too.
FilePrinter.print(FilePrinter.AUTOBAN_WARNING, MapleCharacter.makeMapleReadable(chr.getName()) + " caused " + fac.name() + " " + reason);
}
@@ -113,7 +114,7 @@ public class AutobanManager {
if (this.timestamp[type] == time) {
this.timestampcounter[type]++;
if (this.timestampcounter[type] >= times) {
if (ServerConstants.USE_AUTOBAN) {
if (YamlConfig.config.server.USE_AUTOBAN) {
chr.getClient().disconnect(false, false);
}

View File

@@ -27,6 +27,7 @@ import client.MapleClient;
public abstract class Command {
protected int rank;
protected String description;
public abstract void execute(MapleClient client, String[] params);
@@ -38,6 +39,14 @@ public abstract class Command {
protected void setDescription(String description) {
this.description = description;
}
public int getRank() {
return rank;
}
public void setRank(int rank) {
this.rank = rank;
}
protected String joinStringFrom(String arr[], int start) {
StringBuilder builder = new StringBuilder();

View File

@@ -62,7 +62,7 @@ public class CommandsExecutor {
return heading == USER_HEADING;
}
private HashMap<String, RegisteredCommand> registeredCommands = new HashMap<>();
private HashMap<String, Command> registeredCommands = new HashMap<>();
private Pair<List<String>, List<String>> levelCommandsCursor;
private List<Pair<List<String>, List<String>>> commandsNameDesc = new ArrayList<>();
@@ -94,7 +94,7 @@ public class CommandsExecutor {
private void handleInternal(MapleClient client, String message){
if (client.getPlayer().getMapId() == 300000012) {
client.getPlayer().yellowMessage("You not have permission to use this command while in jail.");
client.getPlayer().yellowMessage("You do not have permission to use commands while in jail.");
return;
}
final String splitRegex = "[ ]";
@@ -107,13 +107,13 @@ public class CommandsExecutor {
final String commandName = splitedMessage[0].toLowerCase();
final String[] lowercaseParams = splitedMessage[1].toLowerCase().split(splitRegex);
final RegisteredCommand command = registeredCommands.get(commandName);
final Command command = registeredCommands.get(commandName);
if (command == null){
client.getPlayer().yellowMessage("Command '" + commandName + "' is not available. See @commands for a list of available commands.");
return;
}
if (client.getPlayer().gmLevel() < command.getRank()){
client.getPlayer().yellowMessage("You not have permission to use this command.");
client.getPlayer().yellowMessage("You do not have permission to use this command.");
return;
}
String[] params;
@@ -122,16 +122,9 @@ public class CommandsExecutor {
} else {
params = new String[]{};
}
try {
Command commandInstance = command.getCommandClass().newInstance();
commandInstance.execute(client, params);
writeLog(client, message);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
command.execute(client, params);
writeLog(client, message);
}
private void writeLog(MapleClient client, String command){
@@ -172,11 +165,19 @@ public class CommandsExecutor {
return;
}
RegisteredCommand registeredCommand = new RegisteredCommand(commandClass, rank);
String commandName = syntax.toLowerCase();
addCommandInfo(commandName, commandClass);
registeredCommands.put(commandName, registeredCommand);
try {
Command commandInstance = commandClass.newInstance(); // thanks Halcyon for noticing commands getting reinstanced every call
commandInstance.setRank(rank);
registeredCommands.put(commandName, commandInstance);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private void registerLv0Commands(){
@@ -253,6 +254,7 @@ public class CommandsExecutor {
addCommand("drop", 2, ItemDropCommand.class);
addCommand("level", 2, LevelCommand.class);
addCommand("levelpro", 2, LevelProCommand.class);
addCommand("setslot", 2, SetSlotCommand.class);
addCommand("setstat", 2, SetStatCommand.class);
addCommand("maxstat", 2, MaxStatCommand.class);
addCommand("maxskill", 2, MaxSkillCommand.class);
@@ -264,6 +266,7 @@ public class CommandsExecutor {
addCommand("unbug", 2, UnBugCommand.class);
addCommand("id", 2, IdCommand.class);
addCommand("gachalist", GachaListCommand.class);
addCommand("loot", LootCommand.class);
commandsNameDesc.add(levelCommandsCursor);
}

View File

@@ -25,7 +25,7 @@ package client.command.commands.gm0;
import client.MapleClient;
import client.command.Command;
import client.processor.BuybackProcessor;
import client.processor.action.BuybackProcessor;
public class BuyBackCommand extends Command {
{

View File

@@ -26,6 +26,7 @@ package client.command.commands.gm0;
import client.command.Command;
import client.MapleClient;
import scripting.npc.NPCScriptManager;
import scripting.quest.QuestScriptManager;
import tools.MaplePacketCreator;
public class DisposeCommand extends Command {
@@ -36,6 +37,7 @@ public class DisposeCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
NPCScriptManager.getInstance().dispose(c);
QuestScriptManager.getInstance().dispose(c);
c.announce(MaplePacketCreator.enableActions());
c.removeClickedNPC();
c.getPlayer().message("You've been disposed.");

View File

@@ -25,7 +25,7 @@ package client.command.commands.gm0;
import client.MapleClient;
import client.command.Command;
import constants.ServerConstants;
import config.YamlConfig;
public class DropLimitCommand extends Command {
{
@@ -35,10 +35,10 @@ public class DropLimitCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
int dropCount = c.getPlayer().getMap().getDroppedItemCount();
if(((float) dropCount) / ServerConstants.ITEM_LIMIT_ON_MAP < 0.75f) {
c.getPlayer().showHint("Current drop count: #b" + dropCount + "#k / #e" + ServerConstants.ITEM_LIMIT_ON_MAP + "#n", 300);
if(((float) dropCount) / YamlConfig.config.server.ITEM_LIMIT_ON_MAP < 0.75f) {
c.getPlayer().showHint("Current drop count: #b" + dropCount + "#k / #e" + YamlConfig.config.server.ITEM_LIMIT_ON_MAP + "#n", 300);
} else {
c.getPlayer().showHint("Current drop count: #r" + dropCount + "#k / #e" + ServerConstants.ITEM_LIMIT_ON_MAP + "#n", 300);
c.getPlayer().showHint("Current drop count: #r" + dropCount + "#k / #e" + YamlConfig.config.server.ITEM_LIMIT_ON_MAP + "#n", 300);
}
}

View File

@@ -25,7 +25,7 @@ package client.command.commands.gm0;
import client.command.Command;
import client.MapleClient;
import net.server.coordinator.MapleLoginBypassCoordinator;
import net.server.coordinator.login.MapleLoginBypassCoordinator;
public class EnableAuthCommand extends Command {
{

View File

@@ -26,7 +26,8 @@ package client.command.commands.gm0;
import client.command.Command;
import client.MapleCharacter;
import client.MapleClient;
import constants.ServerConstants;
import config.YamlConfig;
import server.maps.MapleMap;
public class MapOwnerClaimCommand extends Command {
{
@@ -39,14 +40,27 @@ public class MapOwnerClaimCommand extends Command {
try {
MapleCharacter chr = c.getPlayer();
if (ServerConstants.USE_MAP_OWNERSHIP_SYSTEM) {
if (YamlConfig.config.server.USE_MAP_OWNERSHIP_SYSTEM) {
if (chr.getEventInstance() == null) {
if (chr.getMap().unclaimOwnership(chr)) {
chr.dropMessage(5, "This lawn is now free real estate.");
} else if (chr.getMap().claimOwnership(chr)) {
chr.dropMessage(5, "You have leased this lawn for a while, until you leave here or after 1 minute of inactivity.");
MapleMap map = chr.getMap();
if (map.countBosses() == 0) { // thanks Conrad for suggesting bosses prevent map leasing
MapleMap ownedMap = chr.getOwnedMap(); // thanks Conrad for suggesting not unlease a map as soon as player exits it
if (ownedMap != null) {
ownedMap.unclaimOwnership(chr);
if (map == ownedMap) {
chr.dropMessage(5, "This lawn is now free real estate.");
return;
}
}
if (map.claimOwnership(chr)) {
chr.dropMessage(5, "You have leased this lawn for a while, until you leave here or after 1 minute of inactivity.");
} else {
chr.dropMessage(5, "This lawn has already been leased by a player.");
}
} else {
chr.dropMessage(5, "This lawn has already been leased by another player.");
chr.dropMessage(5, "This lawn is currently under a boss siege.");
}
} else {
chr.dropMessage(5, "This lawn cannot be leased.");

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm0;
import client.MapleCharacter;
import client.command.Command;
import client.MapleClient;
import constants.ServerConstants;
import config.YamlConfig;
public class RatesCommand extends Command {
{
@@ -43,7 +43,7 @@ public class RatesCommand extends Command {
showMsg_ += "MESO Rate: #e#b" + player.getMesoRate() + "x#k#n" + "\r\n";
showMsg_ += "DROP Rate: #e#b" + player.getDropRate() + "x#k#n" + "\r\n";
showMsg_ += "BOSS DROP Rate: #e#b" + player.getBossDropRate() + "x#k#n" + "\r\n";
if(ServerConstants.USE_QUEST_RATE) showMsg_ += "QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n";
if(YamlConfig.config.server.USE_QUEST_RATE) showMsg_ += "QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n";
player.showHint(showMsg_, 300);
}

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm0;
import client.MapleCharacter;
import client.command.Command;
import client.MapleClient;
import constants.ServerConstants;
import config.YamlConfig;
public class ShowRatesCommand extends Command {
{
@@ -60,7 +60,7 @@ public class ShowRatesCommand extends Command {
if(player.getCouponDropRate() != 1) showMsg += "Coupon DROP Rate: #k" + player.getCouponDropRate() + "x#k" + "\r\n";
showMsg += "BOSS DROP Rate: #e#b" + player.getBossDropRate() + "x#k#n" + "\r\n";
if(ServerConstants.USE_QUEST_RATE) {
if(YamlConfig.config.server.USE_QUEST_RATE) {
showMsg += "\r\n" + "#eQUEST RATE#n" + "\r\n";
showMsg += "World QUEST Rate: #e#b" + c.getWorldServer().getQuestRate() + "x#k#n" + "\r\n";
}

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm0;
import client.MapleCharacter;
import client.MapleClient;
import client.command.Command;
import constants.ServerConstants;
import config.YamlConfig;
public class StatDexCommand extends Command {
{
@@ -47,10 +47,10 @@ public class StatDexCommand extends Command {
return;
}
} else {
amount = Math.min(remainingAp, ServerConstants.MAX_AP - player.getDex());
amount = Math.min(remainingAp, YamlConfig.config.server.MAX_AP - player.getDex());
}
if (!player.assignDex(Math.max(amount, 0))) {
player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute.");
player.dropMessage("Please make sure your AP is not over " + YamlConfig.config.server.MAX_AP + " and you have enough to distribute.");
}
}
}

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm0;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ServerConstants;
import config.YamlConfig;
public class StatIntCommand extends Command {
{
@@ -47,10 +47,10 @@ public class StatIntCommand extends Command {
return;
}
} else {
amount = Math.min(remainingAp, ServerConstants.MAX_AP - player.getInt());
amount = Math.min(remainingAp, YamlConfig.config.server.MAX_AP - player.getInt());
}
if (!player.assignInt(Math.max(amount, 0))) {
player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute.");
player.dropMessage("Please make sure your AP is not over " + YamlConfig.config.server.MAX_AP + " and you have enough to distribute.");
}
}
}

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm0;
import client.MapleCharacter;
import client.MapleClient;
import client.command.Command;
import constants.ServerConstants;
import config.YamlConfig;
public class StatLukCommand extends Command {
{
@@ -47,10 +47,10 @@ public class StatLukCommand extends Command {
return;
}
} else {
amount = Math.min(remainingAp, ServerConstants.MAX_AP - player.getLuk());
amount = Math.min(remainingAp, YamlConfig.config.server.MAX_AP - player.getLuk());
}
if (!player.assignLuk(Math.max(amount, 0))) {
player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute.");
player.dropMessage("Please make sure your AP is not over " + YamlConfig.config.server.MAX_AP + " and you have enough to distribute.");
}
}
}

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm0;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ServerConstants;
import config.YamlConfig;
public class StatStrCommand extends Command {
{
@@ -46,11 +46,11 @@ public class StatStrCommand extends Command {
return;
}
} else {
amount = Math.min(remainingAp, ServerConstants.MAX_AP - player.getStr());
amount = Math.min(remainingAp, YamlConfig.config.server.MAX_AP - player.getStr());
}
if (!player.assignStr(Math.max(amount, 0))) {
player.dropMessage("Please make sure your AP is not over " + ServerConstants.MAX_AP + " and you have enough to distribute.");
player.dropMessage("Please make sure your AP is not over " + YamlConfig.config.server.MAX_AP + " and you have enough to distribute.");
}
}
}

View File

@@ -25,7 +25,7 @@ package client.command.commands.gm0;
import client.MapleClient;
import client.command.Command;
import constants.ServerConstants;
import constants.net.ServerConstants;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

View File

@@ -26,14 +26,13 @@ package client.command.commands.gm1;
import client.MapleCharacter;
import client.command.Command;
import client.MapleClient;
import constants.GameConstants;
import constants.game.GameConstants;
import java.util.ArrayList;
import java.util.Collections;
import net.server.Server;
import server.MaplePortal;
import server.maps.MaplePortal;
import server.maps.FieldLimit;
import server.maps.MapleMap;
import server.maps.MapleMapManager;
import server.maps.MapleMapFactory;
import server.maps.MapleMiniDungeonInfo;
import java.util.Comparator;
@@ -47,19 +46,28 @@ public class GotoCommand extends Command {
{
setDescription("");
MapleMapManager mapManager = Server.getInstance().getWorlds().get(0).getChannels().get(0).getMapFactory();
List<Entry<String, Integer>> towns = new ArrayList<>(GameConstants.GOTO_TOWNS.entrySet());
sortGotoEntries(towns);
for (Map.Entry<String, Integer> e : towns) {
GOTO_TOWNS_INFO += ("'" + e.getKey() + "' - #b" + (mapManager.getMap(e.getValue()).getMapName()) + "#k\r\n");
try {
// thanks shavit for noticing goto areas getting loaded from wz needlessly, only for the name retrieval
for (Map.Entry<String, Integer> e : towns) {
GOTO_TOWNS_INFO += ("'" + e.getKey() + "' - #b" + (MapleMapFactory.loadPlaceName(e.getValue())) + "#k\r\n");
}
List<Entry<String, Integer>> areas = new ArrayList<>(GameConstants.GOTO_AREAS.entrySet());
sortGotoEntries(areas);
for (Map.Entry<String, Integer> e : areas) {
GOTO_AREAS_INFO += ("'" + e.getKey() + "' - #b" + (MapleMapFactory.loadPlaceName(e.getValue())) + "#k\r\n");
}
} catch (Exception e) {
e.printStackTrace();
GOTO_TOWNS_INFO = "(none)";
GOTO_AREAS_INFO = "(none)";
}
List<Entry<String, Integer>> areas = new ArrayList<>(GameConstants.GOTO_AREAS.entrySet());
sortGotoEntries(areas);
for (Map.Entry<String, Integer> e : areas) {
GOTO_AREAS_INFO += ("'" + e.getKey() + "' - #b" + (mapManager.getMap(e.getValue()).getMapName()) + "#k\r\n");
}
}
public static String GOTO_TOWNS_INFO = "";
@@ -105,7 +113,7 @@ public class GotoCommand extends Command {
gotomaps = new HashMap<>(GameConstants.GOTO_AREAS); // distinct map registry for GM/users suggested thanks to Vcoc
gotomaps.putAll(GameConstants.GOTO_TOWNS); // thanks Halcyon (UltimateMors) for pointing out duplicates on listed entries functionality
} else {
gotomaps = new HashMap<>(GameConstants.GOTO_TOWNS);
gotomaps = GameConstants.GOTO_TOWNS;
}
if (gotomaps.containsKey(params[0])) {

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm2;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ServerConstants;
import config.YamlConfig;
public class ApCommand extends Command {
{
@@ -44,7 +44,7 @@ public class ApCommand extends Command {
if (params.length < 2) {
int newAp = Integer.parseInt(params[0]);
if (newAp < 0) newAp = 0;
else if (newAp > ServerConstants.MAX_AP) newAp = ServerConstants.MAX_AP;
else if (newAp > YamlConfig.config.server.MAX_AP) newAp = YamlConfig.config.server.MAX_AP;
player.changeRemainingAp(newAp, false);
} else {
@@ -52,7 +52,7 @@ public class ApCommand extends Command {
if (victim != null) {
int newAp = Integer.parseInt(params[1]);
if (newAp < 0) newAp = 0;
else if (newAp > ServerConstants.MAX_AP) newAp = ServerConstants.MAX_AP;
else if (newAp > YamlConfig.config.server.MAX_AP) newAp = YamlConfig.config.server.MAX_AP;
victim.changeRemainingAp(newAp, false);
} else {

View File

@@ -55,7 +55,7 @@ public class IdCommand extends Command {
}
sb.append(String.format("Results found: #r%d#k | Returned: #b%d#k/100 | Refine search query to improve time.", resultList.size(), count) + "\r\n");
player.getClient().getAbstractPlayerInteraction().npcTalk(9010000, sb.toString());
player.getAbstractPlayerInteraction().npcTalk(9010000, sb.toString());
} else {
player.yellowMessage(String.format("Id not found for item: %s, of type: %s.", queryItem, params[0]));
}

View File

@@ -28,8 +28,8 @@ import client.MapleClient;
import client.MapleCharacter;
import client.inventory.MaplePet;
import client.inventory.manipulator.MapleInventoryManipulator;
import constants.ItemConstants;
import constants.ServerConstants;
import config.YamlConfig;
import constants.inventory.ItemConstants;
import server.MapleItemInformationProvider;
public class ItemCommand extends Command {
@@ -57,7 +57,7 @@ public class ItemCommand extends Command {
short quantity = 1;
if(params.length >= 2) quantity = Short.parseShort(params[1]);
if (ServerConstants.BLOCK_GENERATE_CASH_ITEM && ii.isCash(itemId)) {
if (YamlConfig.config.server.BLOCK_GENERATE_CASH_ITEM && ii.isCash(itemId)) {
player.yellowMessage("You cannot create a cash item with this command.");
return;
}
@@ -77,12 +77,12 @@ public class ItemCommand extends Command {
}
}
byte flag = 0;
short flag = 0;
if(player.gmLevel() < 3) {
flag |= ItemConstants.ACCOUNT_SHARING;
flag |= ItemConstants.UNTRADEABLE;
}
MapleInventoryManipulator.addById(c, itemId, quantity, player.getName(), -1, flag, -1);
}
}

View File

@@ -29,8 +29,8 @@ import client.MapleCharacter;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import constants.ItemConstants;
import constants.ServerConstants;
import config.YamlConfig;
import constants.inventory.ItemConstants;
import server.MapleItemInformationProvider;
public class ItemDropCommand extends Command {
@@ -58,7 +58,7 @@ public class ItemDropCommand extends Command {
short quantity = 1;
if(params.length >= 2) quantity = Short.parseShort(params[1]);
if (ServerConstants.BLOCK_GENERATE_CASH_ITEM && ii.isCash(itemId)) {
if (YamlConfig.config.server.BLOCK_GENERATE_CASH_ITEM && ii.isCash(itemId)) {
player.yellowMessage("You cannot create a cash item with this command.");
return;
}
@@ -75,12 +75,12 @@ public class ItemDropCommand extends Command {
toDrop.setOwner("");
if(player.gmLevel() < 3) {
byte b = toDrop.getFlag();
b |= ItemConstants.ACCOUNT_SHARING;
b |= ItemConstants.UNTRADEABLE;
b |= ItemConstants.SANDBOX;
short f = toDrop.getFlag();
f |= ItemConstants.ACCOUNT_SHARING;
f |= ItemConstants.UNTRADEABLE;
f |= ItemConstants.SANDBOX;
toDrop.setFlag(b);
toDrop.setFlag(f);
toDrop.setOwner("TRIAL-MODE");
}
@@ -102,12 +102,12 @@ public class ItemDropCommand extends Command {
toDrop.setOwner(player.getName());
if(player.gmLevel() < 3) {
byte b = toDrop.getFlag();
b |= ItemConstants.ACCOUNT_SHARING;
b |= ItemConstants.UNTRADEABLE;
b |= ItemConstants.SANDBOX;
short f = toDrop.getFlag();
f |= ItemConstants.ACCOUNT_SHARING;
f |= ItemConstants.UNTRADEABLE;
f |= ItemConstants.SANDBOX;
toDrop.setFlag(b);
toDrop.setFlag(f);
toDrop.setOwner("TRIAL-MODE");
}

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm2;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import server.MaplePortal;
import server.maps.MaplePortal;
import server.maps.MapleMap;
public class JailCommand extends Command {

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm2;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ServerConstants;
import config.YamlConfig;
public class LevelCommand extends Command {
{
@@ -45,7 +45,7 @@ public class LevelCommand extends Command {
player.setLevel(Math.min(Integer.parseInt(params[0]), player.getMaxClassLevel()) - 1);
player.resetPlayerRates();
if (ServerConstants.USE_ADD_RATES_BY_LEVEL) player.setPlayerRates();
if (YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL) player.setPlayerRates();
player.setWorldRates();
player.levelUp(false);

View File

@@ -0,0 +1,51 @@
/*
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
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/>.
*/
/*
@Author: Resinate
*/
package client.command.commands.gm2;
import client.MapleClient;
import client.command.Command;
import java.util.Arrays;
import java.util.List;
import server.maps.MapleMapItem;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
public class LootCommand extends Command {
{
setDescription("Loots all items that belong to you.");
}
@Override
public void execute(MapleClient c, String[] params) {
List<MapleMapObject> items = c.getPlayer().getMap().getMapObjectsInRange(c.getPlayer().getPosition(), Double.POSITIVE_INFINITY, Arrays.asList(MapleMapObjectType.ITEM));
for (MapleMapObject item : items) {
MapleMapItem mapItem = (MapleMapItem) item;
if (mapItem.getOwnerId() == c.getPlayer().getId() || mapItem.getOwnerId() == c.getPlayer().getPartyId()) {
c.getPlayer().pickupItem(mapItem);
}
}
}
}

View File

@@ -27,7 +27,7 @@ import client.MapleStat;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ServerConstants;
import config.YamlConfig;
public class MaxStatCommand extends Command {
{
@@ -40,7 +40,7 @@ public class MaxStatCommand extends Command {
player.loseExp(player.getExp(), false, false);
player.setLevel(255);
player.resetPlayerRates();
if (ServerConstants.USE_ADD_RATES_BY_LEVEL) player.setPlayerRates();
if (YamlConfig.config.server.USE_ADD_RATES_BY_LEVEL) player.setPlayerRates();
player.setWorldRates();
player.updateStrDexIntLuk(Short.MAX_VALUE);
player.setFame(13337);

View File

@@ -28,7 +28,7 @@ import client.command.Command;
import client.MapleClient;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import constants.ItemConstants;
import constants.inventory.ItemConstants;
import server.MapleItemInformationProvider;
public class RechargeCommand extends Command {

View File

@@ -0,0 +1,54 @@
/*
This file is part of the HeavenMS MapleStory Server, commands OdinMS-based
Copyleft (L) 2016 - 2019 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/>.
*/
/*
@Author: Ronan
*/
package client.command.commands.gm2;
import client.*;
import client.command.Command;
public class SetSlotCommand extends Command {
{
setDescription("");
}
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
if (params.length < 1) {
player.yellowMessage("Syntax: !setslot <newlevel>");
return;
}
int slots = (Integer.parseInt(params[0]) / 4) * 4;
for (int i = 1; i < 5; i++) {
int curSlots = player.getSlots(i);
if (slots <= -curSlots) {
continue;
}
player.gainSlots(i, slots - curSlots, true);
}
player.yellowMessage("Slots updated.");
}
}

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm2;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ServerConstants;
import config.YamlConfig;
public class SpCommand extends Command {
{
@@ -44,7 +44,7 @@ public class SpCommand extends Command {
if (params.length == 1) {
int newSp = Integer.parseInt(params[0]);
if (newSp < 0) newSp = 0;
else if (newSp > ServerConstants.MAX_AP) newSp = ServerConstants.MAX_AP;
else if (newSp > YamlConfig.config.server.MAX_AP) newSp = YamlConfig.config.server.MAX_AP;
player.updateRemainingSp(newSp);
} else {
@@ -52,7 +52,7 @@ public class SpCommand extends Command {
if (victim != null) {
int newSp = Integer.parseInt(params[1]);
if (newSp < 0) newSp = 0;
else if (newSp > ServerConstants.MAX_AP) newSp = ServerConstants.MAX_AP;
else if (newSp > YamlConfig.config.server.MAX_AP) newSp = YamlConfig.config.server.MAX_AP;
victim.updateRemainingSp(newSp);

View File

@@ -27,7 +27,7 @@ import client.MapleStat;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ItemConstants;
import constants.inventory.ItemConstants;
import server.MapleItemInformationProvider;
public class FaceCommand extends Command {

View File

@@ -27,7 +27,7 @@ import client.MapleStat;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.ItemConstants;
import constants.inventory.ItemConstants;
import server.MapleItemInformationProvider;
public class HairCommand extends Command {

View File

@@ -26,7 +26,7 @@ package client.command.commands.gm3;
import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import constants.GameConstants;
import constants.game.GameConstants;
import tools.MaplePacketCreator;
public class MusicCommand extends Command {

View File

@@ -46,13 +46,13 @@ public class QuestCompleteCommand extends Command {
if (player.getQuestStatus(questId) == 1) {
MapleQuest quest = MapleQuest.getInstance(questId);
if (quest != null) {
int npcid = quest.getNpcRequirement(true);
quest.forceComplete(player, npcid);
player.dropMessage(5, "QUEST " + questId + " completed.");
} else { // should not occur
player.dropMessage(5, "QUESTID " + questId + " is invalid.");
if (quest != null && quest.getNpcRequirement(true) != -1) {
c.getAbstractPlayerInteraction().forceCompleteQuest(questId, quest.getNpcRequirement(true));
} else {
c.getAbstractPlayerInteraction().forceCompleteQuest(questId);
}
player.dropMessage(5, "QUEST " + questId + " completed.");
} else {
player.dropMessage(5, "QUESTID " + questId + " not started or already completed.");
}

View File

@@ -46,13 +46,13 @@ public class QuestStartCommand extends Command {
if (player.getQuestStatus(questid) == 0) {
MapleQuest quest = MapleQuest.getInstance(questid);
if (quest != null) {
int npcid = quest.getNpcRequirement(false);
quest.forceStart(player, npcid);
player.dropMessage(5, "QUEST " + questid + " started.");
if (quest != null && quest.getNpcRequirement(false) != -1) {
c.getAbstractPlayerInteraction().forceStartQuest(questid, quest.getNpcRequirement(false));
} else {
player.dropMessage(5, "QUESTID " + questid + " is invalid.");
c.getAbstractPlayerInteraction().forceStartQuest(questid);
}
player.dropMessage(5, "QUEST " + questid + " started.");
} else {
player.dropMessage(5, "QUESTID " + questid + " already started/completed.");
}

View File

@@ -36,6 +36,8 @@ public class PapCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
MapleCharacter player = c.getPlayer();
player.getMap().spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(8510000), player.getPosition());
// thanks Conrad for noticing mobid typo here
player.getMap().spawnMonsterOnGroundBelow(MapleLifeFactory.getMonster(8500001), player.getPosition());
}
}

View File

@@ -30,7 +30,7 @@ import client.inventory.Equip;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import client.inventory.manipulator.MapleInventoryManipulator;
import constants.ItemConstants;
import constants.inventory.ItemConstants;
import server.MapleItemInformationProvider;
public class ProItemCommand extends Command {
@@ -84,7 +84,7 @@ public class ProItemCommand extends Command {
equip.setHp(stat);
equip.setMp(stat);
byte flag = equip.getFlag();
short flag = equip.getFlag();
flag |= ItemConstants.UNTRADEABLE;
equip.setFlag(flag);
}

View File

@@ -29,7 +29,7 @@ import client.MapleCharacter;
import client.inventory.Equip;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import constants.ItemConstants;
import constants.inventory.ItemConstants;
public class SetEqStatCommand extends Command {
{
@@ -68,7 +68,7 @@ public class SetEqStatCommand extends Command {
eq.setStr(newStat);
eq.setLuk(newStat);
byte flag = eq.getFlag();
short flag = eq.getFlag();
flag |= ItemConstants.UNTRADEABLE;
eq.setFlag(flag);

View File

@@ -27,14 +27,13 @@ import client.command.Command;
import client.MapleClient;
import client.MapleCharacter;
import net.server.Server;
import server.MaplePortal;
import server.maps.MaplePortal;
import server.TimerManager;
import server.life.MapleMonster;
import server.life.SpawnPoint;
import server.maps.MapleMapObject;
import server.maps.MapleMapObjectType;
import server.maps.MapleReactor;
import tools.MaplePacketCreator;
import java.awt.*;
import java.util.Arrays;

View File

@@ -23,7 +23,7 @@ import java.util.Collection;
import client.MapleClient;
import client.MapleCharacter;
import client.command.Command;
import constants.GameConstants;
import constants.game.GameConstants;
import net.server.Server;
import net.server.world.World;

View File

@@ -25,7 +25,7 @@ package client.command.commands.gm5;
import client.command.Command;
import client.MapleClient;
import constants.ServerConstants;
import constants.net.ServerConstants;
public class SetCommand extends Command {
{

View File

@@ -25,7 +25,7 @@ package client.command.commands.gm5;
import client.command.Command;
import client.MapleClient;
import constants.ServerConstants;
import config.YamlConfig;
public class ShowMoveLifeCommand extends Command {
{
@@ -34,6 +34,6 @@ public class ShowMoveLifeCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
ServerConstants.USE_DEBUG_SHOW_RCVD_MVLIFE = !ServerConstants.USE_DEBUG_SHOW_RCVD_MVLIFE;
YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE = !YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_MVLIFE;
}
}

View File

@@ -25,7 +25,7 @@ package client.command.commands.gm5;
import client.command.Command;
import client.MapleClient;
import constants.ServerConstants;
import config.YamlConfig;
public class ShowPacketsCommand extends Command {
{
@@ -34,6 +34,6 @@ public class ShowPacketsCommand extends Command {
@Override
public void execute(MapleClient c, String[] params) {
ServerConstants.USE_DEBUG_SHOW_RCVD_PACKET = !ServerConstants.USE_DEBUG_SHOW_RCVD_PACKET;
YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET = !YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET;
}
}

View File

@@ -21,7 +21,7 @@ package client.command.commands.gm5;
import client.MapleClient;
import client.command.Command;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.session.MapleSessionCoordinator;
/**
*

View File

@@ -22,7 +22,7 @@ package client.command.commands.gm6;
import client.MapleCharacter;
import client.MapleClient;
import client.command.Command;
import constants.ServerConstants;
import config.YamlConfig;
public class SupplyRateCouponCommand extends Command {
{
@@ -37,7 +37,7 @@ public class SupplyRateCouponCommand extends Command {
return;
}
ServerConstants.USE_SUPPLY_RATE_COUPONS = params[0].compareToIgnoreCase("no") != 0;
player.dropMessage(5, "Rate coupons are now " + (ServerConstants.USE_SUPPLY_RATE_COUPONS ? "enabled" : "disabled") + " for purchase at the Cash Shop.");
YamlConfig.config.server.USE_SUPPLY_RATE_COUPONS = params[0].compareToIgnoreCase("no") != 0;
player.dropMessage(5, "Rate coupons are now " + (YamlConfig.config.server.USE_SUPPLY_RATE_COUPONS ? "enabled" : "disabled") + " for purchase at the Cash Shop.");
}
}

View File

@@ -52,7 +52,6 @@ public class WarpWorldCommand extends Command {
String[] socket = server.getInetSocket(worldb, c.getChannel());
c.getWorldServer().removePlayer(player);
player.getMap().removePlayer(player);//LOL FORGOT THIS ><
c.updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
player.setSessionTransitionState();
player.setWorld(worldb);
player.saveCharToDB();//To set the new world :O (true because else 2 player instances are created, one in both worlds)

View File

@@ -25,7 +25,7 @@ import client.MapleSkinColor;
import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import constants.ServerConstants;
import config.YamlConfig;
import net.server.Server;
import server.MapleItemInformationProvider;
import tools.FilePrinter;
@@ -38,7 +38,7 @@ import tools.MaplePacketCreator;
public abstract class CharacterFactory {
protected synchronized static int createNewCharacter(MapleClient c, String name, int face, int hair, int skin, int gender, CharacterFactoryRecipe recipe) {
if (ServerConstants.COLLECTIVE_CHARSLOT ? c.getAvailableCharacterSlots() <= 0 : c.getAvailableCharacterWorldSlots() <= 0) {
if (YamlConfig.config.server.COLLECTIVE_CHARSLOT ? c.getAvailableCharacterSlots() <= 0 : c.getAvailableCharacterWorldSlots() <= 0) {
return -3;
}

View File

@@ -23,6 +23,7 @@ import client.MapleJob;
import client.Skill;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import config.YamlConfig;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -54,6 +55,15 @@ public class CharacterFactoryRecipe {
this.bottom = bottom;
this.shoes = shoes;
this.weapon = weapon;
if (!YamlConfig.config.server.USE_STARTING_AP_4) {
if (YamlConfig.config.server.USE_AUTOASSIGN_STARTERS_AP) {
str = 12;
dex = 5;
} else {
ap = 9;
}
}
}
public void setStr(int v) {

View File

@@ -22,9 +22,9 @@
package client.inventory;
import client.MapleClient;
import constants.ServerConstants;
import constants.ExpTable;
import constants.ItemConstants;
import config.YamlConfig;
import constants.game.ExpTable;
import constants.inventory.ItemConstants;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
@@ -64,7 +64,8 @@ public class Equip extends Item {
}
private byte upgradeSlots;
private byte level, flag, itemLevel;
private byte level, itemLevel;
private short flag;
private short str, dex, _int, luk, hp, mp, watk, matk, wdef, mdef, acc, avoid, hands, speed, jump, vicious;
private float itemExp;
private int ringid = -1;
@@ -117,7 +118,7 @@ public class Equip extends Item {
}
@Override
public byte getFlag() {
public short getFlag() {
return flag;
}
@@ -195,7 +196,7 @@ public class Equip extends Item {
}
@Override
public void setFlag(byte flag) {
public void setFlag(short flag) {
this.flag = flag;
}
@@ -278,7 +279,7 @@ public class Equip extends Item {
private static int getStatModifier(boolean isAttribute) {
// each set of stat points grants a chance for a bonus stat point upgrade at equip level up.
if(ServerConstants.USE_EQUIPMNT_LVLUP_POWER) {
if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_POWER) {
if(isAttribute) return 2;
else return 4;
}
@@ -289,7 +290,7 @@ public class Equip extends Item {
}
private static int randomizeStatUpgrade(int top) {
int limit = Math.min(top, ServerConstants.MAX_EQUIPMNT_LVLUP_STAT_UP);
int limit = Math.min(top, YamlConfig.config.server.MAX_EQUIPMNT_LVLUP_STAT_UP);
int poolCount = (limit * (limit + 1) / 2) + limit;
int rnd = Randomizer.rand(0, poolCount);
@@ -382,7 +383,7 @@ public class Equip extends Item {
public Pair<String, Pair<Boolean, Boolean>> gainStats(List<Pair<StatUpgrade, Integer>> stats) {
boolean gotSlot = false, gotVicious = false;
String lvupStr = "";
Integer statUp, maxStat = ServerConstants.MAX_EQUIPMNT_STAT;
Integer statUp, maxStat = YamlConfig.config.server.MAX_EQUIPMNT_STAT;
for (Pair<StatUpgrade, Integer> stat : stats) {
switch (stat.getLeft()) {
case incDEX:
@@ -482,7 +483,7 @@ public class Equip extends Item {
}
if(!stats.isEmpty()) {
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
@@ -490,7 +491,7 @@ public class Equip extends Item {
isUpgradeable = false;
improveDefaultStats(stats);
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
@@ -498,7 +499,7 @@ public class Equip extends Item {
if(isUpgradeable) {
while(stats.isEmpty()) {
improveDefaultStats(stats);
if(ServerConstants.USE_EQUIPMNT_LVLUP_SLOTS) {
if(YamlConfig.config.server.USE_EQUIPMNT_LVLUP_SLOTS) {
if(vicious > 0) getUnitSlotUpgrade(stats, StatUpgrade.incVicious);
getUnitSlotUpgrade(stats, StatUpgrade.incSlot);
}
@@ -562,14 +563,14 @@ public class Equip extends Item {
return;
}
int equipMaxLevel = Math.min(30, Math.max(ii.getEquipLevel(this.getItemId(), true), ServerConstants.USE_EQUIPMNT_LVLUP));
int equipMaxLevel = Math.min(30, Math.max(ii.getEquipLevel(this.getItemId(), true), YamlConfig.config.server.USE_EQUIPMNT_LVLUP));
if (itemLevel >= equipMaxLevel) {
return;
}
int reqLevel = ii.getEquipLevelReq(this.getItemId());
float masteryModifier = (float)(ServerConstants.EQUIP_EXP_RATE * ExpTable.getExpNeededForLevel(1)) / (float)normalizedMasteryExp(reqLevel);
float masteryModifier = (float)(YamlConfig.config.server.EQUIP_EXP_RATE * ExpTable.getExpNeededForLevel(1)) / (float)normalizedMasteryExp(reqLevel);
float elementModifier = (isElemental) ? 0.85f : 0.6f;
float baseExpGain = gain * elementModifier * masteryModifier;
@@ -577,7 +578,7 @@ public class Equip extends Item {
itemExp += baseExpGain;
int expNeeded = ExpTable.getEquipExpNeededForLevel(itemLevel);
if(ServerConstants.USE_DEBUG_SHOW_INFO_EQPEXP) System.out.println("'" + ii.getName(this.getItemId()) + "' -> EXP Gain: " + gain + " Mastery: " + masteryModifier + " Base gain: " + baseExpGain + " exp: " + itemExp + " / " + expNeeded + ", Kills TNL: " + expNeeded / (baseExpGain / c.getPlayer().getExpRate()));
if(YamlConfig.config.server.USE_DEBUG_SHOW_INFO_EQPEXP) System.out.println("'" + ii.getName(this.getItemId()) + "' -> EXP Gain: " + gain + " Mastery: " + masteryModifier + " Base gain: " + baseExpGain + " exp: " + itemExp + " / " + expNeeded + ", Kills TNL: " + expNeeded / (baseExpGain / c.getPlayer().getExpRate()));
if (itemExp >= expNeeded) {
while(itemExp >= expNeeded) {
@@ -594,7 +595,7 @@ public class Equip extends Item {
}
c.getPlayer().forceUpdateItem(this);
//if(ServerConstants.USE_DEBUG) c.getPlayer().dropMessage("'" + ii.getName(this.getItemId()) + "': " + itemExp + " / " + expNeeded);
//if(YamlConfig.config.server.USE_DEBUG) c.getPlayer().dropMessage("'" + ii.getName(this.getItemId()) + "': " + itemExp + " / " + expNeeded);
}
private boolean reachedMaxLevel() {
@@ -604,7 +605,7 @@ public class Equip extends Item {
}
}
return itemLevel >= ServerConstants.USE_EQUIPMNT_LVLUP;
return itemLevel >= YamlConfig.config.server.USE_EQUIPMNT_LVLUP;
}
public String showEquipFeatures(MapleClient c) {

View File

@@ -21,7 +21,7 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.inventory;
import constants.ItemConstants;
import constants.inventory.ItemConstants;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
@@ -40,7 +40,7 @@ public class Item implements Comparable<Item> {
private MaplePet pet = null;
private String owner = "";
protected List<String> log;
private byte flag;
private short flag;
private long expiration = -1;
private String giftFrom = "";
@@ -126,7 +126,7 @@ public class Item implements Comparable<Item> {
public int getPetId() {
return petid;
}
@Override
public int compareTo(Item other) {
if (this.id < other.getItemId()) {
@@ -146,11 +146,16 @@ public class Item implements Comparable<Item> {
return Collections.unmodifiableList(log);
}
public byte getFlag() {
public short getFlag() {
return flag;
}
public void setFlag(byte b) {
public void setFlag(short b) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
if (ii.isAccountRestricted(id)) {
b |= ItemConstants.ACCOUNT_SHARING; // thanks Shinigami15 for noticing ACCOUNT_SHARING flag not being applied properly to items server-side
}
this.flag = b;
}

View File

@@ -97,7 +97,7 @@ public enum ItemFactory {
equip.setInt((short) rs.getInt("int"));
equip.setJump((short) rs.getInt("jump"));
equip.setVicious((short) rs.getInt("vicious"));
equip.setFlag((byte) rs.getInt("flag"));
equip.setFlag((short) rs.getInt("flag"));
equip.setLuk((short) rs.getInt("luk"));
equip.setMatk((short) rs.getInt("matk"));
equip.setMdef((short) rs.getInt("mdef"));
@@ -173,11 +173,16 @@ public enum ItemFactory {
if (mit.equals(MapleInventoryType.EQUIP) || mit.equals(MapleInventoryType.EQUIPPED)) {
items.add(new Pair<Item, MapleInventoryType>(loadEquipFromResultSet(rs), mit));
} else {
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), rs.getInt("petid"));
int petid = rs.getInt("petid");
if (rs.wasNull()) {
petid = -1;
}
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short) rs.getInt("quantity"), petid);
item.setOwner(rs.getString("owner"));
item.setExpiration(rs.getLong("expiration"));
item.setGiftFrom(rs.getString("giftFrom"));
item.setFlag((byte) rs.getInt("flag"));
item.setFlag((short) rs.getInt("flag"));
items.add(new Pair<>(item, mit));
}
}
@@ -229,7 +234,7 @@ public enum ItemFactory {
ps.setInt(6, item.getPosition());
ps.setInt(7, item.getQuantity());
ps.setString(8, item.getOwner());
ps.setInt(9, item.getPetId());
ps.setInt(9, item.getPetId()); // thanks Daddy Egg for alerting a case of unique petid constraint breach getting raised
ps.setInt(10, item.getFlag());
ps.setLong(11, item.getExpiration());
ps.setString(12, item.getGiftFrom());
@@ -329,11 +334,16 @@ public enum ItemFactory {
items.add(new Pair<Item, MapleInventoryType>(loadEquipFromResultSet(rs), mit));
} else {
if(bundles > 0) {
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), rs.getInt("petid"));
int petid = rs.getInt("petid");
if (rs.wasNull()) {
petid = -1;
}
Item item = new Item(rs.getInt("itemid"), (byte) rs.getInt("position"), (short)(bundles * rs.getInt("quantity")), petid);
item.setOwner(rs.getString("owner"));
item.setExpiration(rs.getLong("expiration"));
item.setGiftFrom(rs.getString("giftFrom"));
item.setFlag((byte) rs.getInt("flag"));
item.setFlag((short) rs.getInt("flag"));
items.add(new Pair<>(item, mit));
}
}

View File

@@ -37,7 +37,7 @@ import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import tools.Pair;
import client.MapleCharacter;
import client.MapleClient;
import constants.ItemConstants;
import constants.inventory.ItemConstants;
import server.MapleItemInformationProvider;
import client.inventory.manipulator.MapleInventoryManipulator;
import tools.FilePrinter;
@@ -83,6 +83,19 @@ public class MapleInventory implements Iterable<Item> {
public void setSlotLimit(int newLimit) {
lock.lock();
try {
if (newLimit < slotLimit) {
List<Short> toRemove = new LinkedList<>();
for (Item it : list()) {
if (it.getPosition() > newLimit) {
toRemove.add(it.getPosition());
}
}
for (Short slot : toRemove) {
removeSlot(slot);
}
}
slotLimit = (byte) newLimit;
} finally {
lock.unlock();

View File

@@ -21,7 +21,7 @@
*/
package client.inventory;
import constants.ExpTable;
import constants.game.ExpTable;
import java.awt.Point;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
@@ -98,75 +98,11 @@ public class MaplePet extends Item {
}
}
private static void unreferenceMissingPetsFromInventoryDb() {
PreparedStatement ps = null;
Connection con = null;
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("UPDATE inventoryitems SET petid = -1, expiration = 0 WHERE petid != -1 AND petid NOT IN (SELECT petid FROM pets)");
ps.executeUpdate();
ps.close();
con.close();
} catch(SQLException ex) {
ex.printStackTrace();
} finally {
try {
if(ps != null && !ps.isClosed()) {
ps.close();
}
if(con != null && !con.isClosed()) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
private static void deleteMissingPetsFromDb() {
PreparedStatement ps = null;
Connection con = null;
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("DELETE FROM pets WHERE petid NOT IN (SELECT petid FROM inventoryitems WHERE petid != -1)");
ps.executeUpdate();
ps.close();
con.close();
} catch(SQLException ex) {
ex.printStackTrace();
} finally {
try {
if(ps != null && !ps.isClosed()) {
ps.close();
}
if(con != null && !con.isClosed()) {
con.close();
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public static void clearMissingPetsFromDb() {
unreferenceMissingPetsFromInventoryDb();
deleteMissingPetsFromDb();
}
public static void deleteFromDb(MapleCharacter owner, int petid) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE `petid` = ?");
ps.setInt(1, petid);
ps.executeUpdate();
ps.close();
ps = con.prepareStatement("DELETE FROM petignores WHERE `petid` = ?"); // thanks Vcoc for detecting petignores remaining after deletion
PreparedStatement ps = con.prepareStatement("DELETE FROM pets WHERE `petid` = ?"); // thanks Vcoc for detecting petignores remaining after deletion
ps.setInt(1, petid);
ps.executeUpdate();
ps.close();

View File

@@ -41,7 +41,10 @@ public class MapleCashidGenerator {
ResultSet rs = ps.executeQuery();
while (rs.next()) {
existentCashids.add(rs.getInt(1));
int id = rs.getInt(1);
if (!rs.wasNull()) {
existentCashids.add(id);
}
}
rs.close();

View File

@@ -31,8 +31,8 @@ import client.inventory.MapleInventoryType;
import client.inventory.MaplePet;
import client.inventory.ModifyInventory;
import client.newyear.NewYearCardRecord;
import constants.ItemConstants;
import constants.ServerConstants;
import config.YamlConfig;
import constants.inventory.ItemConstants;
import java.awt.Point;
import java.util.ArrayList;
@@ -68,29 +68,20 @@ public class MapleInventoryManipulator {
return addById(c, itemId, quantity, owner, petid, (byte) 0, expiration);
}
public static boolean addById(MapleClient c, int itemId, short quantity, String owner, int petid, byte flag, long expiration) {
public static boolean addById(MapleClient c, int itemId, short quantity, String owner, int petid, short flag, long expiration) {
MapleCharacter chr = c.getPlayer();
MapleInventoryType type = ItemConstants.getInventoryType(itemId);
if (c.tryacquireClient()) {
try {
MapleInventory inv = chr.getInventory(type);
inv.lockInventory();
try {
return addByIdInternal(c, chr, type, inv, itemId, quantity, owner, petid, flag, expiration);
} finally {
inv.unlockInventory();
}
} finally {
c.releaseClient();
}
} else {
c.announce(MaplePacketCreator.enableActions());
return false;
MapleInventory inv = chr.getInventory(type);
inv.lockInventory();
try {
return addByIdInternal(c, chr, type, inv, itemId, quantity, owner, petid, flag, expiration);
} finally {
inv.unlockInventory();
}
}
private static boolean addByIdInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, int itemId, short quantity, String owner, int petid, byte flag, long expiration) {
private static boolean addByIdInternal(MapleClient c, MapleCharacter chr, MapleInventoryType type, MapleInventory inv, int itemId, short quantity, String owner, int petid, short flag, long expiration) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
if (!type.equals(MapleInventoryType.EQUIP)) {
short slotMax = ii.getSlotMax(c, itemId);
@@ -187,21 +178,12 @@ public class MapleInventoryManipulator {
MapleCharacter chr = c.getPlayer();
MapleInventoryType type = item.getInventoryType();
if (c.tryacquireClient()) {
try {
MapleInventory inv = chr.getInventory(type);
inv.lockInventory();
try {
return addFromDropInternal(c, chr, type, inv, item, show, petId);
} finally {
inv.unlockInventory();
}
} finally {
c.releaseClient();
}
} else {
c.announce(MaplePacketCreator.enableActions());
return false;
MapleInventory inv = chr.getInventory(type);
inv.lockInventory();
try {
return addFromDropInternal(c, chr, type, inv, item, show, petId);
} finally {
inv.unlockInventory();
}
}
@@ -683,9 +665,27 @@ public class MapleInventoryManipulator {
c.announce(MaplePacketCreator.modifyInventory(true, Collections.singletonList(new ModifyInventory(2, source, src))));
chr.equipChanged();
}
private static boolean isDisappearingItemDrop(Item it) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
if (ii.isDropRestricted(it.getItemId())) {
return true;
} else if (ii.isCash(it.getItemId())) {
if (YamlConfig.config.server.USE_ENFORCE_UNMERCHABLE_CASH) { // thanks Ari for noticing cash drops not available server-side
return true;
} else if (ItemConstants.isPet(it.getItemId()) && YamlConfig.config.server.USE_ENFORCE_UNMERCHABLE_PET) {
return true;
}
} else if (isDroppedItemRestricted(it)) {
return true;
} else if (ItemConstants.isWeddingRing(it.getItemId())) {
return true;
}
return false;
}
public static void drop(MapleClient c, MapleInventoryType type, short src, short quantity) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
if (src < 0) {
type = MapleInventoryType.EQUIPPED;
}
@@ -698,14 +698,21 @@ public class MapleInventoryManipulator {
return;
}
int itemId = source.getItemId();
if (ItemConstants.isPet(itemId)) {
return;
}
MapleMap map = chr.getMap();
if ((!ItemConstants.isRechargeable(itemId) && source.getQuantity() < quantity) || quantity < 0) {
return;
}
int petid = source.getPetId();
if (petid > -1) {
int petIdx = chr.getPetIndex(petid);
if(petIdx > -1) {
MaplePet pet = chr.getPet(petIdx);
chr.unequipPet(pet, true);
}
}
Point dropPos = new Point(chr.getPosition());
if (quantity < source.getQuantity() && !ItemConstants.isRechargeable(itemId)) {
Item target = source.copy();
@@ -721,11 +728,9 @@ public class MapleInventoryManipulator {
NewYearCardRecord.removeAllNewYearCard(false, chr);
c.getAbstractPlayerInteraction().removeAll(4301000);
}
} else if (ItemConstants.isWeddingRing(source.getItemId())) {
map.disappearingItemDrop(chr, chr, target, dropPos);
}
if (ii.isDropRestricted(target.getItemId()) || ii.isCash(target.getItemId()) || isDroppedItemRestricted(target)) {
if (isDisappearingItemDrop(target)) {
map.disappearingItemDrop(chr, chr, target, dropPos);
} else {
map.spawnItemDrop(chr, chr, target, dropPos, true, true);
@@ -754,11 +759,9 @@ public class MapleInventoryManipulator {
NewYearCardRecord.removeAllNewYearCard(false, chr);
c.getAbstractPlayerInteraction().removeAll(4301000);
}
} else if (ItemConstants.isWeddingRing(source.getItemId())) {
map.disappearingItemDrop(chr, chr, source, dropPos);
}
if (ii.isDropRestricted(itemId) || ii.isCash(itemId) || isDroppedItemRestricted(source)) {
if (isDisappearingItemDrop(source)) {
map.disappearingItemDrop(chr, chr, source, dropPos);
} else {
map.spawnItemDrop(chr, chr, source, dropPos, true, true);
@@ -781,7 +784,7 @@ public class MapleInventoryManipulator {
}
private static boolean isDroppedItemRestricted(Item it) {
return ServerConstants.USE_ERASE_UNTRADEABLE_DROP && it.isUntradeable();
return YamlConfig.config.server.USE_ERASE_UNTRADEABLE_DROP && it.isUntradeable();
}
public static boolean isSandboxItem(Item it) {

View File

@@ -19,7 +19,7 @@
*/
package client.inventory.manipulator;
import constants.ItemConstants;
import constants.inventory.ItemConstants;
import client.inventory.Item;
/**
@@ -27,18 +27,18 @@ import client.inventory.Item;
* @author RonanLana
*/
public class MapleKarmaManipulator {
private static int getKarmaFlag(Item item) {
private static short getKarmaFlag(Item item) {
return item.getItemType() == 1 ? ItemConstants.KARMA_EQP : ItemConstants.KARMA_USE;
}
public static boolean hasKarmaFlag(Item item) {
int karmaFlag = getKarmaFlag(item);
short karmaFlag = getKarmaFlag(item);
return (item.getFlag() & karmaFlag) == karmaFlag;
}
public static void toggleKarmaFlagToUntradeable(Item item) {
int karmaFlag = getKarmaFlag(item);
int flag = item.getFlag();
short karmaFlag = getKarmaFlag(item);
short flag = item.getFlag();
if ((flag & karmaFlag) == karmaFlag) {
flag ^= karmaFlag;
@@ -49,8 +49,8 @@ public class MapleKarmaManipulator {
}
public static void setKarmaFlag(Item item) {
int karmaFlag = getKarmaFlag(item);
int flag = item.getFlag();
short karmaFlag = getKarmaFlag(item);
short flag = item.getFlag();
flag |= karmaFlag;
flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE);

View File

@@ -17,7 +17,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.processor;
package client.processor.action; // thanks Alex for pointing out some package structures containing broad modules
import client.MapleClient;
import client.MapleCharacter;

View File

@@ -17,22 +17,23 @@
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 client.processor;
package client.processor.action;
import client.MapleClient;
import client.MapleCharacter;
import client.inventory.Equip;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import constants.ItemConstants;
import constants.ServerConstants;
import client.inventory.manipulator.MapleInventoryManipulator;
import constants.GameConstants;
import config.YamlConfig;
import constants.inventory.ItemConstants;
import constants.game.GameConstants;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import server.MakerItemFactory;
import server.MakerItemFactory.MakerItemCreateEntry;
import server.MapleItemInformationProvider;
import tools.FilePrinter;
import tools.MaplePacketCreator;
@@ -44,6 +45,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
* @author Ronan
*/
public class MakerProcessor {
private static MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
public static void makerAction(SeekableLittleEndianAccessor slea, MapleClient c) {
@@ -54,7 +56,7 @@ public class MakerProcessor {
int toDisassemble = -1, pos = -1;
boolean makerSucceeded = true;
MakerItemFactory.MakerItemCreateEntry recipe;
MakerItemCreateEntry recipe;
Map<Integer, Short> reagentids = new LinkedHashMap<>();
int stimulantid = -1;
@@ -62,29 +64,31 @@ public class MakerProcessor {
int fromLeftover = toCreate;
toCreate = ii.getMakerCrystalFromLeftover(toCreate);
if(toCreate == -1) {
c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal conversion."));
c.announce(MaplePacketCreator.serverNotice(1, ii.getName(fromLeftover) + " is unavailable for Monster Crystal conversion."));
c.announce(MaplePacketCreator.makerEnableActions());
return;
}
recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover);
recipe = MakerItemFactory.generateLeftoverCrystalEntry(fromLeftover, toCreate);
} else if(type == 4) { // disassembling
slea.readInt(); // 1... probably inventory type
pos = slea.readInt();
Item it = c.getPlayer().getInventory(MapleInventoryType.EQUIP).getItem((short) pos);
if(it != null && it.getItemId() == toCreate) {
Pair<Integer, Integer> p;
if((p = generateDisassemblyInfo(toCreate)) != null) {
recipe = MakerItemFactory.generateDisassemblyCrystalEntry(p.getLeft(), p.getRight());
toDisassemble = toCreate;
toCreate = ii.getMakerCrystalFromEquip(toCreate);
toDisassemble = toCreate;
Pair<Integer, List<Pair<Integer, Integer>>> p = generateDisassemblyInfo(toDisassemble);
if(p != null) {
recipe = MakerItemFactory.generateDisassemblyCrystalEntry(toDisassemble, p.getLeft(), p.getRight());
} else {
c.announce(MaplePacketCreator.serverNotice(1, ii.getName(toCreate) + " is unavailable for Monster Crystal disassembly."));
c.announce(MaplePacketCreator.makerEnableActions());
return;
}
} else {
c.announce(MaplePacketCreator.serverNotice(1, "An unknown error occurred when trying to apply that item for disassembly."));
c.announce(MaplePacketCreator.makerEnableActions());
return;
}
} else {
@@ -132,6 +136,7 @@ public class MakerProcessor {
if(!reagentids.isEmpty()) {
if(!removeOddMakerReagents(toCreate, reagentids)) {
c.announce(MaplePacketCreator.serverNotice(1, "You can only use WATK and MATK Strengthening Gems on weapon items."));
c.announce(MaplePacketCreator.makerEnableActions());
return;
}
}
@@ -146,65 +151,80 @@ public class MakerProcessor {
case -1:// non-available for Maker itemid has been tried to forge
FilePrinter.printError(FilePrinter.EXPLOITS, "Player " + c.getPlayer().getName() + " tried to craft itemid " + toCreate + " using the Maker skill.");
c.announce(MaplePacketCreator.serverNotice(1, "The requested item could not be crafted on this operation."));
c.announce(MaplePacketCreator.makerEnableActions());
break;
case 1: // no items
c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + "."));
c.announce(MaplePacketCreator.serverNotice(1, "You don't have all required items in your inventory to make " + ii.getName(toCreate) + "."));
c.announce(MaplePacketCreator.makerEnableActions());
break;
case 2: // no meso
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough mesos (" + GameConstants.numberWithCommas(recipe.getCost()) + ") to complete this operation."));
c.announce(MaplePacketCreator.makerEnableActions());
break;
case 3: // no req level
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough level to complete this operation."));
c.announce(MaplePacketCreator.makerEnableActions());
break;
case 4: // no req skill level
c.announce(MaplePacketCreator.serverNotice(1, "You don't have enough Maker level to complete this operation."));
c.announce(MaplePacketCreator.makerEnableActions());
break;
case 5: // inventory full
c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full."));
c.announce(MaplePacketCreator.makerEnableActions());
break;
default:
if (MapleInventoryManipulator.checkSpace(c, toCreate, (short) recipe.getRewardAmount(), "")) {
if(toDisassemble != -1) {
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false);
} else {
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight());
c.getAbstractPlayerInteraction().gainItem(p.getLeft(), (short) -p.getRight(), false);
}
}
if(toDisassemble != -1) {
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.EQUIP, (short) pos, (short) 1, false);
c.announce(MaplePacketCreator.getShowItemGain(toDisassemble, (short) -1, true));
}
int cost = recipe.getCost();
if(stimulantid == -1 && reagentids.isEmpty()) {
if(cost > 0) c.getPlayer().gainMeso(-cost);
int cost = recipe.getCost();
if(stimulantid == -1 && reagentids.isEmpty()) {
if(cost > 0) c.getPlayer().gainMeso(-cost, false);
for (Pair<Integer, Integer> p : recipe.getGainItems()) {
c.getPlayer().setCS(true);
c.getAbstractPlayerInteraction().gainItem(toCreate, (short) recipe.getRewardAmount());
c.getAbstractPlayerInteraction().gainItem(p.getLeft(), p.getRight().shortValue(), false);
c.getPlayer().setCS(false);
} else {
if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1);
if(!reagentids.isEmpty()) {
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue()));
}
}
if(cost > 0) c.getPlayer().gainMeso(-cost);
makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids);
}
if(makerSucceeded) c.announce(MaplePacketCreator.serverNotice(1, "You have successfully created " + recipe.getRewardAmount() + " " + ii.getName(toCreate) + "."));
else c.getPlayer().dropMessage(5, "The Maker skill lights up, but the skill winds up as if nothing happened.");
c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded));
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false);
if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) {
c.getAbstractPlayerInteraction().setQuestProgress(6033, 1);
}
} else {
c.announce(MaplePacketCreator.serverNotice(1, "Your inventory is full."));
toCreate = recipe.getGainItems().get(0).getLeft();
if(stimulantid != -1) c.getAbstractPlayerInteraction().gainItem(stimulantid, (short) -1, false);
if(!reagentids.isEmpty()) {
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
c.getAbstractPlayerInteraction().gainItem(r.getKey(), (short) (-1 * r.getValue()), false);
}
}
if(cost > 0) c.getPlayer().gainMeso(-cost, false);
makerSucceeded = addBoostedMakerItem(c, toCreate, stimulantid, reagentids);
}
// thanks inhyuk for noticing missing MAKER_RESULT packets
if (type == 3) {
c.announce(MaplePacketCreator.makerResultCrystal(recipe.getGainItems().get(0).getLeft(), recipe.getReqItems().get(0).getLeft()));
} else if (type == 4) {
c.announce(MaplePacketCreator.makerResultDesynth(recipe.getReqItems().get(0).getLeft(), recipe.getCost(), recipe.getGainItems()));
} else {
c.announce(MaplePacketCreator.makerResult(makerSucceeded, recipe.getGainItems().get(0).getLeft(), recipe.getGainItems().get(0).getRight(), recipe.getCost(), recipe.getReqItems(), stimulantid, new LinkedList<>(reagentids.keySet())));
}
c.announce(MaplePacketCreator.showMakerEffect(makerSucceeded));
c.getPlayer().getMap().broadcastMessage(c.getPlayer(), MaplePacketCreator.showForeignMakerEffect(c.getPlayer().getId(), makerSucceeded), false);
if(toCreate == 4260003 && type == 3 && c.getPlayer().getQuestStatus(6033) == 1) {
c.getAbstractPlayerInteraction().setQuestProgress(6033, 1);
}
}
} finally {
@@ -218,7 +238,7 @@ public class MakerProcessor {
Map<Integer, Integer> reagentType = new LinkedHashMap<>();
List<Integer> toRemove = new LinkedList<>();
boolean isWeapon = ItemConstants.isWeapon(toCreate) || ServerConstants.USE_MAKER_PERMISSIVE_ATKUP; // thanks Vcoc for finding a case where a weapon wouldn't be counted as such due to a bounding on isWeapon
boolean isWeapon = ItemConstants.isWeapon(toCreate) || YamlConfig.config.server.USE_MAKER_PERMISSIVE_ATKUP; // thanks Vcoc for finding a case where a weapon wouldn't be counted as such due to a bounding on isWeapon
for(Map.Entry<Integer, Short> r : reagentids.entrySet()) {
int curRid = r.getKey();
@@ -271,12 +291,12 @@ public class MakerProcessor {
}
}
private static Pair<Integer, Integer> generateDisassemblyInfo(int itemId) {
private static Pair<Integer, List<Pair<Integer, Integer>>> generateDisassemblyInfo(int itemId) {
int recvFee = ii.getMakerDisassembledFee(itemId);
if(recvFee > -1) {
int recvQty = ii.getMakerDisassembledQuantity(itemId);
if(recvQty > 0) {
return new Pair<>(recvFee, recvQty);
List<Pair<Integer, Integer>> gains = ii.getMakerDisassembledItems(itemId);
if(!gains.isEmpty()) {
return new Pair<>(recvFee, gains);
}
}
@@ -287,7 +307,7 @@ public class MakerProcessor {
return chr.getSkillLevel((chr.getJob().getId() / 1000) * 10000000 + 1007);
}
private static short getCreateStatus(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) {
private static short getCreateStatus(MapleClient c, MakerItemCreateEntry recipe) {
if(recipe == null) {
return -1;
}
@@ -308,10 +328,29 @@ public class MakerProcessor {
return 4;
}
List<Integer> addItemids = new LinkedList<>();
List<Integer> addQuantity = new LinkedList<>();
List<Integer> rmvItemids = new LinkedList<>();
List<Integer> rmvQuantity = new LinkedList<>();
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
rmvItemids.add(p.getLeft());
rmvQuantity.add(p.getRight());
}
for (Pair<Integer, Integer> p : recipe.getGainItems()) {
addItemids.add(p.getLeft());
addQuantity.add(p.getRight());
}
if (!c.getAbstractPlayerInteraction().canHoldAllAfterRemoving(addItemids, addQuantity, rmvItemids, rmvQuantity)) {
return 5;
}
return 0;
}
private static boolean hasItems(MapleClient c, MakerItemFactory.MakerItemCreateEntry recipe) {
private static boolean hasItems(MapleClient c, MakerItemCreateEntry recipe) {
for (Pair<Integer, Integer> p : recipe.getReqItems()) {
int itemId = p.getLeft();
if (c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).countById(itemId) < p.getRight()) {
@@ -332,8 +371,8 @@ public class MakerProcessor {
Equip eqp = (Equip)item;
if(ItemConstants.isAccessory(item.getItemId()) && eqp.getUpgradeSlots() <= 0) eqp.setUpgradeSlots(3);
if(ServerConstants.USE_ENHANCED_CRAFTING == true) {
if(!(c.getPlayer().isGM() && ServerConstants.USE_PERFECT_GM_SCROLL)) {
if(YamlConfig.config.server.USE_ENHANCED_CRAFTING == true) {
if(!(c.getPlayer().isGM() && YamlConfig.config.server.USE_PERFECT_GM_SCROLL)) {
eqp.setUpgradeSlots((byte)(eqp.getUpgradeSlots() + 1));
}
item = MapleItemInformationProvider.getInstance().scrollEquipWithId(eqp, 2049100, true, 2049100, c.getPlayer().isGM());
@@ -397,7 +436,6 @@ public class MakerProcessor {
}
MapleInventoryManipulator.addFromDrop(c, item, false, -1);
c.announce(MaplePacketCreator.getShowItemGain(itemid, (short) 1, true));
return true;
}
}

View File

@@ -0,0 +1,190 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
Copyleft (L) 2016 - 2019 RonanLana (HeavenMS)
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.processor.action;
import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.manipulator.MapleInventoryManipulator;
import config.YamlConfig;
import java.util.List;
import server.MapleItemInformationProvider;
import server.MapleStatEffect;
import tools.MaplePacketCreator;
/**
*
* @author Ronan - multi-pot consumption feature
*/
public class PetAutopotProcessor {
private static class AutopotAction {
private MapleClient c;
private short slot;
private int itemId;
private Item toUse;
private List<Item> toUseList;
private boolean hasHpGain, hasMpGain;
private int maxHp, maxMp, curHp, curMp;
private double incHp, incMp;
private boolean cursorOnNextAvailablePot(MapleCharacter chr) {
if(toUseList == null) {
toUseList = chr.getInventory(MapleInventoryType.USE).linkedListById(itemId);
}
toUse = null;
while(!toUseList.isEmpty()) {
Item it = toUseList.remove(0);
if(it.getQuantity() > 0) {
toUse = it;
slot = it.getPosition();
return true;
}
}
return false;
}
public AutopotAction(MapleClient c, short slot, int itemId) {
this.c = c;
this.slot = slot;
this.itemId = itemId;
}
public void run() {
MapleClient c = this.c;
MapleCharacter chr = c.getPlayer();
if (!chr.isAlive()) {
c.announce(MaplePacketCreator.enableActions());
return;
}
int useCount = 0, qtyCount = 0;
MapleStatEffect stat = null;
maxHp = chr.getCurrentMaxHp();
maxMp = chr.getCurrentMaxMp();
curHp = chr.getHp();
curMp = chr.getMp();
MapleInventory useInv = chr.getInventory(MapleInventoryType.USE);
useInv.lockInventory();
try {
toUse = useInv.getItem(slot);
if (toUse != null) {
if (toUse.getItemId() != itemId) {
c.announce(MaplePacketCreator.enableActions());
return;
}
toUseList = null;
// from now on, toUse becomes the "cursor" for the current pot being used
if (toUse.getQuantity() <= 0) {
if (!cursorOnNextAvailablePot(chr)) {
c.announce(MaplePacketCreator.enableActions());
return;
}
}
stat = MapleItemInformationProvider.getInstance().getItemEffect(toUse.getItemId());
hasHpGain = stat.getHp() > 0 || stat.getHpRate() > 0.0;
hasMpGain = stat.getMp() > 0 || stat.getMpRate() > 0.0;
incHp = stat.getHp();
if(incHp <= 0 && hasHpGain) incHp = Math.ceil(maxHp * stat.getHpRate());
incMp = stat.getMp();
if(incMp <= 0 && hasMpGain) incMp = Math.ceil(maxMp * stat.getMpRate());
if (YamlConfig.config.server.USE_COMPULSORY_AUTOPOT) {
if (hasHpGain) {
double hpRatio = (YamlConfig.config.server.PET_AUTOHP_RATIO * maxHp) - curHp;
if (hpRatio > 0.0) {
qtyCount = (int) Math.ceil(hpRatio / incHp);
}
}
if (hasMpGain) {
double mpRatio = ((YamlConfig.config.server.PET_AUTOMP_RATIO * maxMp) - curMp);
if (mpRatio > 0.0) {
qtyCount = Math.max(qtyCount, (int) Math.ceil(mpRatio / incMp));
}
}
if (qtyCount < 0) { // thanks Flint, Kevs for noticing an issue where negative counts were getting achieved
qtyCount = 0;
}
} else {
qtyCount = 1; // non-compulsory autopot concept thanks to marcuswoon
}
while (true) {
short qtyToUse = (short) Math.min(qtyCount, toUse.getQuantity());
MapleInventoryManipulator.removeFromSlot(c, MapleInventoryType.USE, slot, qtyToUse, false);
curHp += (incHp * qtyToUse);
curMp += (incMp * qtyToUse);
useCount += qtyToUse;
qtyCount -= qtyToUse;
if(toUse.getQuantity() == 0 && qtyCount > 0) {
// depleted out the current slot, fetch for more
if(!cursorOnNextAvailablePot(chr)) {
break; // no more pots available
}
} else {
break; // gracefully finished it's job, quit the loop
}
}
}
} finally {
useInv.unlockInventory();
}
if (stat != null) {
for (int i = 0; i < useCount; i++) {
stat.applyTo(chr);
}
}
chr.announce(MaplePacketCreator.enableActions());
}
}
public static void runAutopotAction(MapleClient c, short slot, int itemid) {
AutopotAction action = new AutopotAction(c, slot, itemid);
action.run();
}
}

View File

@@ -17,7 +17,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.processor;
package client.processor.action;
import client.MapleCharacter;
import java.awt.Point;
@@ -62,7 +62,7 @@ public class SpawnPetProcessor {
long expiration = chr.getInventory(MapleInventoryType.CASH).getItem(slot).getExpiration();
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, petid, (short) 1, false, false);
MapleInventoryManipulator.addById(c, evolveid, (short) 1, null, petId, expiration);
MaplePet.deleteFromDb(chr, petid);
c.announce(MaplePacketCreator.enableActions());
return;
}

View File

@@ -21,7 +21,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.processor;
package client.processor.npc;
import client.MapleCharacter;
import client.MapleClient;
@@ -32,13 +32,14 @@ import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.manipulator.MapleInventoryManipulator;
import client.inventory.manipulator.MapleKarmaManipulator;
import constants.ItemConstants;
import constants.ServerConstants;
import config.YamlConfig;
import constants.inventory.ItemConstants;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Collections;
import java.util.LinkedList;
@@ -59,6 +60,7 @@ import tools.Pair;
public class DueyProcessor {
public enum Actions {
TOSERVER_RECV_ITEM(0x00),
TOSERVER_SEND_ITEM(0x02),
TOSERVER_CLAIM_PACKAGE(0x04),
TOSERVER_REMOVE_PACKAGE(0x05),
@@ -112,19 +114,6 @@ public class DueyProcessor {
return null;
}
private static String getCurrentDate() {
String date = "";
Calendar cal = Calendar.getInstance();
int day = cal.get(Calendar.DATE) - 1; // instant duey ?
int month = cal.get(Calendar.MONTH) + 1; // its an array of months.
int year = cal.get(Calendar.YEAR);
date += day <= 9 ? "0" + day + "-" : "" + day + "-";
date += month <= 9 ? "0" + month + "-" : "" + month + "-";
date += year;
return date;
}
private static void showDueyNotification(MapleClient c, MapleCharacter player) {
Connection con = null;
PreparedStatement ps = null;
@@ -132,7 +121,7 @@ public class DueyProcessor {
ResultSet rs = null;
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("SELECT Mesos FROM dueypackages WHERE ReceiverId = ? and Checked = 1");
ps = con.prepareStatement("SELECT SenderName, Type FROM dueypackages WHERE ReceiverId = ? AND Checked = 1 ORDER BY Type DESC");
ps.setInt(1, player.getId());
rs = ps.executeQuery();
if (rs.next()) {
@@ -143,11 +132,11 @@ public class DueyProcessor {
pss.executeUpdate();
pss.close();
con2.close();
c.announce(MaplePacketCreator.sendDueyParcelReceived(rs.getString("SenderName"), rs.getInt("Type") == 1));
} catch (SQLException e) {
e.printStackTrace();
}
c.announce(MaplePacketCreator.sendDueyNotification(false));
}
} catch (SQLException e) {
e.printStackTrace();
@@ -207,7 +196,7 @@ public class DueyProcessor {
dueypack.setSender(rs.getString("SenderName"));
dueypack.setMesos(rs.getInt("Mesos"));
dueypack.setSentTime(rs.getString("TimeStamp"));
dueypack.setSentTime(rs.getTimestamp("TimeStamp"), rs.getBoolean("Type"));
dueypack.setMessage(rs.getString("Message"));
return dueypack;
@@ -241,7 +230,7 @@ public class DueyProcessor {
return packages;
}
private static int createPackage(int mesos, String message, String sender, int toCid) {
private static int createPackage(int mesos, String message, String sender, int toCid, boolean quick) {
try {
Connection con = null;
PreparedStatement ps = null;
@@ -249,16 +238,17 @@ public class DueyProcessor {
try {
con = DatabaseConnection.getConnection();
ps = con.prepareStatement("INSERT INTO `dueypackages` (ReceiverId, SenderName, Mesos, TimeStamp, Message, Checked) VALUES (?, ?, ?, ?, ?, 1)", Statement.RETURN_GENERATED_KEYS);
ps = con.prepareStatement("INSERT INTO `dueypackages` (ReceiverId, SenderName, Mesos, TimeStamp, Message, Type, Checked) VALUES (?, ?, ?, ?, ?, ?, 1)", Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, toCid);
ps.setString(2, sender);
ps.setInt(3, mesos);
ps.setString(4, getCurrentDate());
ps.setTimestamp(4, new Timestamp(System.currentTimeMillis()));
ps.setString(5, message);
ps.setInt(6, quick ? 1 : 0);
int updateRows = ps.executeUpdate();
if (updateRows < 1) {
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Error trying to create package [mesos: " + mesos + ", " + sender + ", to CharacterId: " + toCid + "]");
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Error trying to create package [mesos: " + mesos + ", " + sender + ", quick: " + quick + ", to CharacterId: " + toCid + "]");
return -1;
}
@@ -267,7 +257,7 @@ public class DueyProcessor {
if (rs.next()) {
packageId = rs.getInt(1);
} else {
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Failed inserting package [mesos: " + mesos + ", " + sender + ", to CharacterId: " + toCid + "]");
FilePrinter.printError(FilePrinter.INSERT_CHAR, "Failed inserting package [mesos: " + mesos + ", " + sender + ", quick: " + quick + ", to CharacterId: " + toCid + "]");
return -1;
}
@@ -348,10 +338,18 @@ public class DueyProcessor {
return 0;
}
public static void dueySendItem(MapleClient c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient) {
public static void dueySendItem(MapleClient c, byte invTypeId, short itemPos, short amount, int sendMesos, String sendMessage, String recipient, boolean quick) {
if (c.tryacquireClient()) {
try {
final int fee = 5000 + MapleTrade.getFee(sendMesos);
int fee = MapleTrade.getFee(sendMesos);
if (!quick) {
fee += 5000;
} else if (!c.getPlayer().haveItem(5330000)) {
AutobanFactory.PACKET_EDIT.alert(c.getPlayer(), c.getPlayer().getName() + " tried to packet edit with Quick Delivery on duey.");
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use duey with Quick Delivery, mesos " + sendMesos + " and amount " + amount);
c.disconnect(true, false);
return;
}
long finalcost = (long) sendMesos + fee;
if (finalcost < 0 || finalcost > Integer.MAX_VALUE || (amount < 1 && sendMesos == 0)) {
@@ -385,7 +383,11 @@ public class DueyProcessor {
return;
}
int packageId = createPackage(sendMesos, sendMessage, c.getPlayer().getName(), recipientCid);
if (quick) {
MapleInventoryManipulator.removeById(c, MapleInventoryType.CASH, 5330000, (short) 1, false, false);
}
int packageId = createPackage(sendMesos, sendMessage, c.getPlayer().getName(), recipientCid, quick);
if (packageId == -1) {
c.announce(MaplePacketCreator.sendDueyMSG(DueyProcessor.Actions.TOCLIENT_SEND_ENABLE_ACTIONS.getCode()));
return;
@@ -451,11 +453,16 @@ public class DueyProcessor {
}
con.close();
if(dp == null) {
if (dp == null) {
c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode()));
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to receive package from duey with id " + packageId);
return;
}
if (dp.isDeliveringTime()) {
c.announce(MaplePacketCreator.sendDueyMSG(Actions.TOCLIENT_RECV_UNKNOWN_ERROR.getCode()));
return;
}
Item dpItem = dp.getItem();
if (dpItem != null) {
@@ -490,10 +497,21 @@ public class DueyProcessor {
}
}
public static void dueySendTalk(MapleClient c) {
public static void dueySendTalk(MapleClient c, boolean quickDelivery) {
if (c.tryacquireClient()) {
try {
c.announce(MaplePacketCreator.sendDuey((byte) 8, loadPackages(c.getPlayer())));
long timeNow = System.currentTimeMillis();
if(timeNow - c.getPlayer().getNpcCooldown() < YamlConfig.config.server.BLOCK_NPC_RACE_CONDT) {
c.announce(MaplePacketCreator.enableActions());
return;
}
c.getPlayer().setNpcCooldown(timeNow);
if (quickDelivery) {
c.announce(MaplePacketCreator.sendDuey(0x1A, null));
} else {
c.announce(MaplePacketCreator.sendDuey(0x8, loadPackages(c.getPlayer())));
}
} finally {
c.releaseClient();
}
@@ -501,9 +519,43 @@ public class DueyProcessor {
}
public static void dueyCreatePackage(Item item, int mesos, String sender, int recipientCid) {
int packageId = createPackage(mesos, "", sender, recipientCid);
int packageId = createPackage(mesos, null, sender, recipientCid, false);
if (packageId != -1) {
insertPackageItem(packageId, item);
}
}
public static void runDueyExpireSchedule() {
try {
Calendar c = Calendar.getInstance();
c.add(Calendar.DATE, -30);
Timestamp ts = new Timestamp(c.getTime().getTime());
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT `PackageId` FROM dueypackages WHERE `TimeStamp` < ?");
ps.setTimestamp(1, ts);
List<Integer> toRemove = new LinkedList<>();
try (ResultSet rs = ps.executeQuery()) {
while (rs.next()) {
toRemove.add(rs.getInt("PackageId"));
}
}
ps.close();
for (Integer pid : toRemove) {
removePackageFromDB(pid);
}
ps = con.prepareStatement("DELETE FROM dueypackages WHERE `TimeStamp` < ?");
ps.setTimestamp(1, ts);
ps.executeUpdate();
ps.close();
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}

View File

@@ -21,7 +21,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.processor;
package client.processor.npc;
import client.MapleCharacter;
import client.MapleClient;
@@ -37,7 +37,6 @@ import java.sql.Timestamp;
import java.util.LinkedList;
import java.util.List;
import client.inventory.manipulator.MapleInventoryManipulator;
import constants.ServerConstants;
import java.util.Collections;
import net.server.Server;
import net.server.world.World;

View File

@@ -19,7 +19,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.processor;
package client.processor.npc;
import client.MapleClient;
import client.MapleCharacter;
@@ -28,8 +28,8 @@ import client.inventory.Item;
import client.inventory.MapleInventory;
import client.inventory.MapleInventoryType;
import client.inventory.manipulator.MapleKarmaManipulator;
import constants.ItemConstants;
import constants.ServerConstants;
import config.YamlConfig;
import constants.inventory.ItemConstants;
import client.inventory.manipulator.MapleInventoryManipulator;
import server.MapleItemInformationProvider;
import server.MapleStorage;
@@ -45,6 +45,7 @@ import tools.data.input.SeekableLittleEndianAccessor;
public class StorageProcessor {
public static void storageAction(SeekableLittleEndianAccessor slea, MapleClient c) {
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
MapleCharacter chr = c.getPlayer();
MapleStorage storage = chr.getStorage();
byte mode = slea.readByte();
@@ -69,7 +70,7 @@ public class StorageProcessor {
slot = storage.getSlot(MapleInventoryType.getByType(type), slot);
Item item = storage.getItem(slot);
if (item != null) {
if (MapleItemInformationProvider.getInstance().isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
if (ii.isPickupRestricted(item.getItemId()) && chr.haveItemWithId(item.getItemId(), true)) {
c.announce(MaplePacketCreator.getStorageError((byte) 0x0C));
return;
}
@@ -83,13 +84,20 @@ public class StorageProcessor {
}
if (MapleInventoryManipulator.checkSpace(c, item.getItemId(), item.getQuantity(), item.getOwner())) {
item = storage.takeOut(slot);//actually the same but idc
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
chr.setUsedStorage();
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
MapleInventoryManipulator.addFromDrop(c, item, false);
storage.sendTakenOut(c, item.getInventoryType());
if (storage.takeOut(item)) {
chr.setUsedStorage();
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
MapleInventoryManipulator.addFromDrop(c, item, false);
String itemName = ii.getName(item.getItemId());
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " took out " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
storage.sendTakenOut(c, item.getInventoryType());
} else {
c.announce(MaplePacketCreator.enableActions());
return;
}
} else {
c.announce(MaplePacketCreator.getStorageError((byte) 0x0A));
}
@@ -149,14 +157,17 @@ public class StorageProcessor {
MapleKarmaManipulator.toggleKarmaFlagToUntradeable(item);
item.setQuantity(quantity);
storage.store(item);
storage.sendStored(c, ItemConstants.getInventoryType(itemId));
String itemName = MapleItemInformationProvider.getInstance().getName(item.getItemId());
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
storage.store(item); // inside a critical section, "!(storage.isFull())" is still in effect...
chr.setUsedStorage();
String itemName = ii.getName(item.getItemId());
FilePrinter.print(FilePrinter.STORAGE + c.getAccountName() + ".txt", c.getPlayer().getName() + " stored " + item.getQuantity() + " " + itemName + " (" + item.getItemId() + ")");
storage.sendStored(c, ItemConstants.getInventoryType(itemId));
}
} else if (mode == 6) { // arrange items
if(ServerConstants.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c);
if(YamlConfig.config.server.USE_STORAGE_ITEM_SORT) storage.arrangeItems(c);
c.announce(MaplePacketCreator.enableActions());
} else if (mode == 7) { // meso
int meso = slea.readInt();
@@ -178,14 +189,14 @@ public class StorageProcessor {
}
storage.setMeso(storageMesos - meso);
chr.gainMeso(meso, false, true, false);
FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos");
chr.setUsedStorage();
FilePrinter.print(FilePrinter.STORAGE + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + (meso > 0 ? " took out " : " stored ") + Math.abs(meso) + " mesos");
storage.sendMeso(c);
} else {
c.announce(MaplePacketCreator.enableActions());
return;
}
storage.sendMeso(c);
} else if (mode == 8) {// close
} else if (mode == 8) {// close... unless the player decides to enter cash shop!
storage.close();
}
} finally {

View File

@@ -21,7 +21,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package client.processor;
package client.processor.stat;
import client.MapleCharacter;
import client.MapleClient;
@@ -33,11 +33,12 @@ import client.autoban.AutobanFactory;
import client.inventory.Equip;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import constants.ServerConstants;
import config.YamlConfig;
import constants.skills.BlazeWizard;
import constants.skills.Brawler;
import constants.skills.DawnWarrior;
import constants.skills.Magician;
import constants.skills.ThunderBreaker;
import constants.skills.Warrior;
import java.util.ArrayList;
import java.util.Collection;
@@ -69,7 +70,7 @@ public class AssignAPProcessor {
int remainingAp = chr.getRemainingAp();
slea.skip(8);
if(ServerConstants.USE_SERVER_AUTOASSIGNER) {
if(YamlConfig.config.server.USE_SERVER_AUTOASSIGNER) {
// --------- Ronan Lana's AUTOASSIGNER ---------
// This method excels for assigning APs in such a way to cover all equipments AP requirements.
byte opt = slea.readByte(); // useful for pirate autoassigning
@@ -134,7 +135,7 @@ public class AssignAPProcessor {
luk = scStat;
str = 0; dex = 0;
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && luk + chr.getLuk() > CAP) {
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && luk + chr.getLuk() > CAP) {
temp = luk + chr.getLuk() - CAP;
scStat -= temp;
prStat += temp;
@@ -160,7 +161,7 @@ public class AssignAPProcessor {
str = scStat;
int_ = 0; luk = 0;
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
temp = str + chr.getStr() - CAP;
scStat -= temp;
prStat += temp;
@@ -186,7 +187,7 @@ public class AssignAPProcessor {
str = scStat;
int_ = 0; luk = 0;
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
temp = str + chr.getStr() - CAP;
scStat -= temp;
prStat += temp;
@@ -240,12 +241,12 @@ public class AssignAPProcessor {
str = trStat;
int_ = 0;
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) {
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) {
temp = dex + chr.getDex() - CAP;
scStat -= temp;
prStat += temp;
}
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && str + chr.getStr() > CAP) {
temp = str + chr.getStr() - CAP;
trStat -= temp;
prStat += temp;
@@ -312,7 +313,7 @@ public class AssignAPProcessor {
dex = scStat;
int_ = 0; luk = 0;
if(ServerConstants.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) {
if(YamlConfig.config.server.USE_AUTOASSIGN_SECONDARY_CAP && dex + chr.getDex() > CAP) {
temp = dex + chr.getDex() - CAP;
scStat -= temp;
prStat += temp;
@@ -347,14 +348,7 @@ public class AssignAPProcessor {
if(slea.available() < 16) {
AutobanFactory.PACKET_EDIT.alert(chr, "Didn't send full packet for Auto Assign.");
final MapleClient client = c;
ThreadManager.getInstance().newTask(new Runnable() {
@Override
public void run() {
client.disconnect(false, false);
}
});
c.disconnect(true, false);
return;
}
@@ -386,44 +380,44 @@ public class AssignAPProcessor {
int newVal = 0;
if (type.equals(MapleStat.STR)) {
newVal = statUpdate[0] + gain;
if (newVal > ServerConstants.MAX_AP) {
statGain[0] += (gain - (newVal - ServerConstants.MAX_AP));
statUpdate[0] = ServerConstants.MAX_AP;
if (newVal > YamlConfig.config.server.MAX_AP) {
statGain[0] += (gain - (newVal - YamlConfig.config.server.MAX_AP));
statUpdate[0] = YamlConfig.config.server.MAX_AP;
} else {
statGain[0] += gain;
statUpdate[0] = newVal;
}
} else if (type.equals(MapleStat.INT)) {
newVal = statUpdate[3] + gain;
if (newVal > ServerConstants.MAX_AP) {
statGain[3] += (gain - (newVal - ServerConstants.MAX_AP));
statUpdate[3] = ServerConstants.MAX_AP;
if (newVal > YamlConfig.config.server.MAX_AP) {
statGain[3] += (gain - (newVal - YamlConfig.config.server.MAX_AP));
statUpdate[3] = YamlConfig.config.server.MAX_AP;
} else {
statGain[3] += gain;
statUpdate[3] = newVal;
}
} else if (type.equals(MapleStat.LUK)) {
newVal = statUpdate[2] + gain;
if (newVal > ServerConstants.MAX_AP) {
statGain[2] += (gain - (newVal - ServerConstants.MAX_AP));
statUpdate[2] = ServerConstants.MAX_AP;
if (newVal > YamlConfig.config.server.MAX_AP) {
statGain[2] += (gain - (newVal - YamlConfig.config.server.MAX_AP));
statUpdate[2] = YamlConfig.config.server.MAX_AP;
} else {
statGain[2] += gain;
statUpdate[2] = newVal;
}
} else if (type.equals(MapleStat.DEX)) {
newVal = statUpdate[1] + gain;
if (newVal > ServerConstants.MAX_AP) {
statGain[1] += (gain - (newVal - ServerConstants.MAX_AP));
statUpdate[1] = ServerConstants.MAX_AP;
if (newVal > YamlConfig.config.server.MAX_AP) {
statGain[1] += (gain - (newVal - YamlConfig.config.server.MAX_AP));
statUpdate[1] = YamlConfig.config.server.MAX_AP;
} else {
statGain[1] += gain;
statUpdate[1] = newVal;
}
}
if (newVal > ServerConstants.MAX_AP) {
return newVal - ServerConstants.MAX_AP;
if (newVal > YamlConfig.config.server.MAX_AP) {
return newVal - YamlConfig.config.server.MAX_AP;
}
return 0;
}
@@ -488,7 +482,7 @@ public class AssignAPProcessor {
}
break;
case 2048: // HP
if(ServerConstants.USE_ENFORCE_HPMP_SWAP) {
if(YamlConfig.config.server.USE_ENFORCE_HPMP_SWAP) {
if (APTo != 8192) {
player.message("You can only swap HP ability points to MP.");
c.announce(MaplePacketCreator.enableActions());
@@ -513,13 +507,13 @@ public class AssignAPProcessor {
int curHp = player.getHp();
int hplose = -takeHp(player.getJob());
player.assignHP(hplose, -1);
if (!ServerConstants.USE_FIXED_RATIO_HPMP_UPDATE) {
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
player.updateHp(Math.max(1, curHp + hplose));
}
break;
case 8192: // MP
if(ServerConstants.USE_ENFORCE_HPMP_SWAP) {
if(YamlConfig.config.server.USE_ENFORCE_HPMP_SWAP) {
if (APTo != 2048) {
player.message("You can only swap MP ability points to HP.");
c.announce(MaplePacketCreator.enableActions());
@@ -557,7 +551,7 @@ public class AssignAPProcessor {
int curMp = player.getMp();
int mplose = -takeMp(job);
player.assignMP(mplose, -1);
if (!ServerConstants.USE_FIXED_RATIO_HPMP_UPDATE) {
if (!YamlConfig.config.server.USE_FIXED_RATIO_HPMP_UPDATE) {
player.updateMp(Math.max(0, curMp + mplose));
}
break;
@@ -646,7 +640,7 @@ public class AssignAPProcessor {
MaxHP += increaseHP.getEffect(sLvl).getY();
}
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if (usedAPReset) {
MaxHP += 20;
} else {
@@ -656,7 +650,7 @@ public class AssignAPProcessor {
MaxHP += 20;
}
} else if(job.isA(MapleJob.ARAN1)) {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if (usedAPReset) {
MaxHP += 20;
} else {
@@ -666,7 +660,7 @@ public class AssignAPProcessor {
MaxHP += 28;
}
} else if (job.isA(MapleJob.MAGICIAN) || job.isA(MapleJob.BLAZEWIZARD1)) {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if (usedAPReset) {
MaxHP += 6;
} else {
@@ -676,7 +670,7 @@ public class AssignAPProcessor {
MaxHP += 6;
}
} else if (job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if (usedAPReset) {
MaxHP += 16;
} else {
@@ -686,7 +680,7 @@ public class AssignAPProcessor {
MaxHP += 16;
}
} else if(job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if (usedAPReset) {
MaxHP += 16;
} else {
@@ -697,14 +691,14 @@ public class AssignAPProcessor {
}
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
if(!usedAPReset) {
Skill increaseHP = SkillFactory.getSkill(Brawler.IMPROVE_MAX_HP);
Skill increaseHP = SkillFactory.getSkill(job.isA(MapleJob.PIRATE) ? Brawler.IMPROVE_MAX_HP : ThunderBreaker.IMPROVE_MAX_HP);
int sLvl = player.getSkillLevel(increaseHP);
if(sLvl > 0)
MaxHP += increaseHP.getEffect(sLvl).getY();
}
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if (usedAPReset) {
MaxHP += 18;
} else {
@@ -716,7 +710,7 @@ public class AssignAPProcessor {
} else if (usedAPReset) {
MaxHP += 8;
} else {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
MaxHP += Randomizer.rand(8, 12);
} else {
MaxHP += 10;
@@ -731,7 +725,7 @@ public class AssignAPProcessor {
int MaxMP = 0;
if (job.isA(MapleJob.WARRIOR) || job.isA(MapleJob.DAWNWARRIOR1) || job.isA(MapleJob.ARAN1)) {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if(!usedAPReset) {
MaxMP += (Randomizer.rand(2, 4) + (player.getInt() / 10));
} else {
@@ -749,7 +743,7 @@ public class AssignAPProcessor {
MaxMP += increaseMP.getEffect(sLvl).getY();
}
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if(!usedAPReset) {
MaxMP += (Randomizer.rand(12, 16) + (player.getInt() / 20));
} else {
@@ -759,7 +753,7 @@ public class AssignAPProcessor {
MaxMP += 18;
}
} else if (job.isA(MapleJob.BOWMAN) || job.isA(MapleJob.WINDARCHER1)) {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if(!usedAPReset) {
MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
} else {
@@ -769,7 +763,7 @@ public class AssignAPProcessor {
MaxMP += 10;
}
} else if(job.isA(MapleJob.THIEF) || job.isA(MapleJob.NIGHTWALKER1)) {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if(!usedAPReset) {
MaxMP += (Randomizer.rand(6, 8) + (player.getInt() / 10));
} else {
@@ -779,7 +773,7 @@ public class AssignAPProcessor {
MaxMP += 10;
}
} else if (job.isA(MapleJob.PIRATE) || job.isA(MapleJob.THUNDERBREAKER1)) {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if(!usedAPReset) {
MaxMP += (Randomizer.rand(7, 9) + (player.getInt() / 10));
} else {
@@ -789,7 +783,7 @@ public class AssignAPProcessor {
MaxMP += 14;
}
} else {
if(ServerConstants.USE_RANDOMIZE_HPMP_GAIN) {
if(YamlConfig.config.server.USE_RANDOMIZE_HPMP_GAIN) {
if(!usedAPReset) {
MaxMP += (Randomizer.rand(4, 6) + (player.getInt() / 10));
} else {

View File

@@ -21,14 +21,14 @@
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 client.processor;
package client.processor.stat;
import client.MapleCharacter;
import client.MapleClient;
import client.Skill;
import client.SkillFactory;
import client.autoban.AutobanFactory;
import constants.GameConstants;
import constants.game.GameConstants;
import constants.skills.Aran;
import server.ThreadManager;
import tools.FilePrinter;
@@ -40,31 +40,35 @@ import tools.MaplePacketCreator;
*/
public class AssignSPProcessor {
public static boolean canSPAssign(MapleClient c, int skillid) {
if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
c.announce(MaplePacketCreator.enableActions());
return false;
}
MapleCharacter player = c.getPlayer();
if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) {
AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.");
c.disconnect(true, false);
return false;
}
return true;
}
public static void SPAssignAction(MapleClient c, int skillid) {
c.lockClient();
try {
if (skillid == Aran.HIDDEN_FULL_DOUBLE || skillid == Aran.HIDDEN_FULL_TRIPLE || skillid == Aran.HIDDEN_OVER_DOUBLE || skillid == Aran.HIDDEN_OVER_TRIPLE) {
c.announce(MaplePacketCreator.enableActions());
if (!canSPAssign(c, skillid)) {
return;
}
MapleCharacter player = c.getPlayer();
int remainingSp = player.getRemainingSps()[GameConstants.getSkillBook(skillid/10000)];
boolean isBeginnerSkill = false;
if ((!GameConstants.isPqSkillMap(player.getMapId()) && GameConstants.isPqSkill(skillid)) || (!player.isGM() && GameConstants.isGMSkills(skillid)) || (!GameConstants.isInJobTree(skillid, player.getJob().getId()) && !player.isGM())) {
AutobanFactory.PACKET_EDIT.alert(player, "tried to packet edit in distributing sp.");
FilePrinter.printError(FilePrinter.EXPLOITS + c.getPlayer().getName() + ".txt", c.getPlayer().getName() + " tried to use skill " + skillid + " without it being in their job.");
final MapleClient client = c;
ThreadManager.getInstance().newTask(new Runnable() {
@Override
public void run() {
client.disconnect(true, false);
}
});
return;
}
if (skillid % 10000000 > 999 && skillid % 10000000 < 1003) {
int total = 0;
for (int i = 0; i < 3; i++) {

View File

@@ -0,0 +1,304 @@
package config;
public class ServerConfig {
//Thread Tracker Configuration
public boolean USE_THREAD_TRACKER;
//Database Configuration
public String DB_URL;
public String DB_USER;
public String DB_PASS;
public boolean DB_CONNECTION_POOL;
//Login Configuration
public int WORLDS;
public int WLDLIST_SIZE;
public int CHANNEL_SIZE;
public int CHANNEL_LOAD;
public int CHANNEL_LOCKS;
public long RESPAWN_INTERVAL;
public long PURGING_INTERVAL;
public long RANKING_INTERVAL;
public long COUPON_INTERVAL;
public long UPDATE_INTERVAL;
public boolean ENABLE_PIC;
public boolean ENABLE_PIN;
public int BYPASS_PIC_EXPIRATION;
public int BYPASS_PIN_EXPIRATION;
public boolean AUTOMATIC_REGISTER;
public boolean BCRYPT_MIGRATION;
public boolean COLLECTIVE_CHARSLOT;
public boolean DETERRED_MULTICLIENT;
//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 int MAX_ALLOWED_ACCOUNT_HWID;
public int MAX_ACCOUNT_LOGIN_ATTEMPT;
public int LOGIN_ATTEMPT_DURATION;
//Ip Configuration
public String HOST;
public boolean LOCALSERVER;
public boolean GMSERVER;
//Other configuration
public boolean SHUTDOWNHOOK;
//Server Flags
public boolean USE_CUSTOM_KEYSET;
public boolean USE_DEBUG;
public boolean USE_DEBUG_SHOW_INFO_EQPEXP;
public boolean USE_DEBUG_SHOW_RCVD_PACKET;
public boolean USE_DEBUG_SHOW_RCVD_MVLIFE;
public boolean USE_DEBUG_SHOW_PACKET;
public boolean USE_SUPPLY_RATE_COUPONS;
public boolean USE_IP_VALIDATION;
public boolean USE_CHARACTER_ACCOUNT_CHECK;
public boolean USE_MAXRANGE;
public boolean USE_MAXRANGE_ECHO_OF_HERO;
public boolean USE_MTS;
public boolean USE_CPQ;
public boolean USE_AUTOHIDE_GM;
public boolean USE_BUYBACK_SYSTEM;
public boolean USE_FIXED_RATIO_HPMP_UPDATE;
public boolean USE_FAMILY_SYSTEM;
public boolean USE_DUEY;
public boolean USE_RANDOMIZE_HPMP_GAIN;
public boolean USE_STORAGE_ITEM_SORT;
public boolean USE_ITEM_SORT;
public boolean USE_ITEM_SORT_BY_NAME;
public boolean USE_PARTY_FOR_STARTERS;
public boolean USE_AUTOASSIGN_STARTERS_AP;
public boolean USE_AUTOASSIGN_SECONDARY_CAP;
public boolean USE_STARTING_AP_4;
public boolean USE_AUTOBAN;
public boolean USE_AUTOBAN_LOG;
public boolean USE_AUTOSAVE;
public boolean USE_SERVER_AUTOASSIGNER;
public boolean USE_REFRESH_RANK_MOVE;
public boolean USE_ENFORCE_ADMIN_ACCOUNT;
public boolean USE_ENFORCE_NOVICE_EXPRATE;
public boolean USE_ENFORCE_HPMP_SWAP;
public boolean USE_ENFORCE_MOB_LEVEL_RANGE;
public boolean USE_ENFORCE_JOB_LEVEL_RANGE;
public boolean USE_ENFORCE_JOB_SP_RANGE;
public boolean USE_ENFORCE_ITEM_SUGGESTION;
public boolean USE_ENFORCE_UNMERCHABLE_CASH;
public boolean USE_ENFORCE_UNMERCHABLE_PET;
public boolean USE_ENFORCE_MERCHANT_SAVE;
public boolean USE_ENFORCE_MDOOR_POSITION;
public boolean USE_SPAWN_CLEAN_MDOOR;
public boolean USE_SPAWN_LOOT_ON_ANIMATION;
public boolean USE_SPAWN_RELEVANT_LOOT;
public boolean USE_ERASE_PERMIT_ON_OPENSHOP;
public boolean USE_ERASE_UNTRADEABLE_DROP;
public boolean USE_ERASE_PET_ON_EXPIRATION;
public boolean USE_BUFF_MOST_SIGNIFICANT;
public boolean USE_BUFF_EVERLASTING;
public boolean USE_MULTIPLE_SAME_EQUIP_DROP;
public boolean USE_BANISHABLE_TOWN_SCROLL;
public boolean USE_ENABLE_FULL_RESPAWN;
public boolean USE_ENABLE_CHAT_LOG;
public boolean USE_REBIRTH_SYSTEM;
public boolean USE_MAP_OWNERSHIP_SYSTEM;
public boolean USE_FISHING_SYSTEM;
public boolean USE_NPCS_SCRIPTABLE;
//Events/PQs Configuration
public boolean USE_OLD_GMS_STYLED_PQ_NPCS;
public boolean USE_ENABLE_SOLO_EXPEDITIONS;
public boolean USE_ENABLE_DAILY_EXPEDITIONS;
public boolean USE_ENABLE_RECALL_EVENT;
//Announcement Configuration
public boolean USE_ANNOUNCE_SHOPITEMSOLD;
public boolean USE_ANNOUNCE_CHANGEJOB;
//Cash Shop Configuration
public boolean USE_JOINT_CASHSHOP_INVENTORY;
public boolean USE_CLEAR_OUTDATED_COUPONS;
public boolean ALLOW_CASHSHOP_NAME_CHANGE;
public boolean ALLOW_CASHSHOP_WORLD_TRANSFER;//Allows players to buy world transfers in the cash shop.
//Maker Configuration
public boolean USE_MAKER_PERMISSIVE_ATKUP;
public boolean USE_MAKER_FEE_HEURISTICS;
//Custom Configuration
public boolean USE_ENABLE_CUSTOM_NPC_SCRIPT;
public boolean USE_STARTER_MERGE;
//Commands Configuration
public boolean BLOCK_GENERATE_CASH_ITEM;
public boolean USE_WHOLE_SERVER_RANKING;
public double EQUIP_EXP_RATE;
public double PQ_BONUS_EXP_RATE;
public byte EXP_SPLIT_LEVEL_INTERVAL;
public byte EXP_SPLIT_LEECH_INTERVAL;
public float EXP_SPLIT_MVP_MOD;
public float EXP_SPLIT_COMMON_MOD;
public float PARTY_BONUS_EXP_RATE;
//Miscellaneous Configuration
public String TIMEZONE;
public boolean USE_DISPLAY_NUMBERS_WITH_COMMA;
public boolean USE_UNITPRICE_WITH_COMMA;
public byte MAX_MONITORED_BUFFSTATS;
public int MAX_AP;
public int MAX_EVENT_LEVELS;
public long BLOCK_NPC_RACE_CONDT;
public int TOT_MOB_QUEST_REQUIREMENT;
public int MOB_REACTOR_REFRESH_TIME;
public int PARTY_SEARCH_REENTRY_LIMIT;
public long NAME_CHANGE_COOLDOWN;
public long WORLD_TRANSFER_COOLDOWN=NAME_CHANGE_COOLDOWN;//Cooldown for world tranfers, default is same as name change (30 days).
public boolean INSTANT_NAME_CHANGE;
//Dangling Items/Locks Configuration
public int ITEM_EXPIRE_TIME ;
public int KITE_EXPIRE_TIME ;
public int ITEM_MONITOR_TIME;
public int LOCK_MONITOR_TIME;
//Map Monitor Configuration
public int ITEM_EXPIRE_CHECK;
public int ITEM_LIMIT_ON_MAP;
public int MAP_VISITED_SIZE;
public int MAP_DAMAGE_OVERTIME_INTERVAL;
//Channel Mob Disease Monitor Configuration
public int MOB_STATUS_MONITOR_PROC;
public int MOB_STATUS_MONITOR_LIFE;
public int MOB_STATUS_AGGRO_PERSISTENCE;
public int MOB_STATUS_AGGRO_INTERVAL;
//Some Gameplay Enhancing Configurations
//Scroll Configuration
public boolean USE_PERFECT_GM_SCROLL;
public boolean USE_PERFECT_SCROLLING;
public boolean USE_ENHANCED_CHSCROLL;
public boolean USE_ENHANCED_CRAFTING;
public boolean USE_ENHANCED_CLNSLATE;
public int SCROLL_CHANCE_ROLLS;
public int CHSCROLL_STAT_RATE;
public int CHSCROLL_STAT_RANGE;
//Beginner Skills Configuration
public boolean USE_ULTRA_NIMBLE_FEET;
public boolean USE_ULTRA_RECOVERY;
public boolean USE_ULTRA_THREE_SNAILS;
//Other Skills Configuration
public boolean USE_FULL_ARAN_SKILLSET;
public boolean USE_FAST_REUSE_HERO_WILL;
public boolean USE_ANTI_IMMUNITY_CRASH;
public boolean USE_UNDISPEL_HOLY_SHIELD;
public boolean USE_FULL_HOLY_SYMBOL;
//Character Configuration
public boolean USE_ADD_SLOTS_BY_LEVEL;
public boolean USE_ADD_RATES_BY_LEVEL;
public boolean USE_STACK_COUPON_RATES;
public boolean USE_PERFECT_PITCH;
//Quest Configuration
public boolean USE_QUEST_RATE;
//Quest Points Configuration
public int QUEST_POINT_REPEATABLE_INTERVAL;
public int QUEST_POINT_REQUIREMENT;
public int QUEST_POINT_PER_QUEST_COMPLETE;
public int QUEST_POINT_PER_EVENT_CLEAR;
//Guild Configuration
public int CREATE_GUILD_MIN_PARTNERS;
public int CREATE_GUILD_COST;
public int CHANGE_EMBLEM_COST;
public int EXPAND_GUILD_BASE_COST;
public int EXPAND_GUILD_TIER_COST;
public int EXPAND_GUILD_MAX_COST;
//Family Configuration
public int FAMILY_REP_PER_KILL;
public int FAMILY_REP_PER_BOSS_KILL;
public int FAMILY_REP_PER_LEVELUP;
public int FAMILY_MAX_GENERATIONS;
//Equipment Configuration
public boolean USE_EQUIPMNT_LVLUP_SLOTS;
public boolean USE_EQUIPMNT_LVLUP_POWER;
public boolean USE_EQUIPMNT_LVLUP_CASH;
public boolean USE_SPIKES_AVOID_BANISH;
public int MAX_EQUIPMNT_LVLUP_STAT_UP;
public int MAX_EQUIPMNT_STAT;
public int USE_EQUIPMNT_LVLUP;
//Map-Chair Configuration
public boolean USE_CHAIR_EXTRAHEAL;
public byte CHAIR_EXTRA_HEAL_MULTIPLIER;
public int CHAIR_EXTRA_HEAL_MAX_DELAY;
//Player NPC Configuration
public int PLAYERNPC_INITIAL_X;
public int PLAYERNPC_INITIAL_Y;
public int PLAYERNPC_AREA_X;
public int PLAYERNPC_AREA_Y;
public int PLAYERNPC_AREA_STEPS;
public boolean PLAYERNPC_ORGANIZE_AREA;
public boolean PLAYERNPC_AUTODEPLOY;
//Pet Auto-Pot Configuration
public boolean USE_COMPULSORY_AUTOPOT;
public boolean USE_EQUIPS_ON_AUTOPOT;
public double PET_AUTOHP_RATIO;
public double PET_AUTOMP_RATIO;
//Pet & Mount Configuration
public byte PET_EXHAUST_COUNT;
public byte MOUNT_EXHAUST_COUNT;
//Pet Hunger Configuration
public boolean PETS_NEVER_HUNGRY;
public boolean GM_PETS_NEVER_HUNGRY;
//Event Configuration
public int EVENT_MAX_GUILD_QUEUE;
public long EVENT_LOBBY_DELAY;
//Dojo Configuration
public boolean USE_FAST_DOJO_UPGRADE;
public boolean USE_DEADLY_DOJO;
public int DOJO_ENERGY_ATK;
public int DOJO_ENERGY_DMG;
//Wedding Configuration
public int WEDDING_RESERVATION_DELAY;
public int WEDDING_RESERVATION_TIMEOUT;
public int WEDDING_RESERVATION_INTERVAL;
public int WEDDING_BLESS_EXP;
public int WEDDING_GIFT_LIMIT;
public boolean WEDDING_BLESSER_SHOWFX;
//Buyback Configuration
public boolean USE_BUYBACK_WITH_MESOS;
public float BUYBACK_FEE;
public float BUYBACK_LEVEL_STACK_FEE;
public int BUYBACK_MESO_MULTIPLIER;
public int BUYBACK_RETURN_MINUTES;
public int BUYBACK_COOLDOWN_MINUTES;
// Login timeout by shavit
public long TIMEOUT_DURATION;
//Event End Timestamp
public long EVENT_END_TIMESTAMP;
}

View File

@@ -0,0 +1,16 @@
package config;
public class WorldConfig {
public int flag = 0;
public String server_message = "Welcome!";
public String event_message = "";
public String why_am_i_recommended = "";
public int channels = 1;
public int exp_rate = 1;
public int meso_rate = 1;
public int drop_rate = 1;
public int boss_drop_rate = 1;
public int quest_rate = 1;
public int travel_rate = 1;
public int fishing_rate = 1;
}

View File

@@ -0,0 +1,32 @@
package config;
import com.esotericsoftware.yamlbeans.YamlReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.util.List;
public class YamlConfig {
public static final YamlConfig config = fromFile("config.yaml");
public List<WorldConfig> worlds;
public ServerConfig server;
public static YamlConfig fromFile(String filename) {
try {
YamlReader reader = new YamlReader(new FileReader(filename));
YamlConfig config = reader.read(YamlConfig.class);
reader.close();
return config;
} catch (FileNotFoundException e) {
String message = "Could not read config file " + filename + ": " + e.getMessage();
throw new RuntimeException(message);
} catch (IOException e) {
String message = "Could not successfully parse config file " + filename + ": " + e.getMessage();
throw new RuntimeException(message);
}
}
}

View File

@@ -1,331 +0,0 @@
package constants;
import java.io.FileInputStream;
import java.util.Properties;
public class ServerConstants {
//Thread Tracker Configuration
public static final boolean USE_THREAD_TRACKER = true; //[SEVERE] This deadlock auditing thing will bloat the memory as fast as the time frame one takes to lose track of a raindrop on a tempesting day. Only for debugging purposes.
//Database Configuration
public static String DB_URL = "";
public static String DB_USER = "";
public static String DB_PASS = "";
public static final boolean DB_CONNECTION_POOL = true; //Installs a connection pool to hub DB connections. Set false to default.
//Server Version
public static short VERSION = 83;
//Login Configuration
public static final int WLDLIST_SIZE = 21; //Max possible worlds on the server.
public static final int CHANNEL_SIZE = 20; //Max possible channels per world (which is 20, based on the channel list on login phase).
public static final int CHANNEL_LOAD = 100; //Max players per channel (limit actually used to calculate the World server capacity).
public static final int CHANNEL_LOCKS = 20; //Total number of structure management locks each channel has.
public static final long RESPAWN_INTERVAL = 10 * 1000; //10 seconds, 10000.
public static final long PURGING_INTERVAL = 5 * 60 * 1000;
public static final long RANKING_INTERVAL = 60 * 60 * 1000; //60 minutes, 3600000.
public static final long COUPON_INTERVAL = 60 * 60 * 1000; //60 minutes, 3600000.
public static final long UPDATE_INTERVAL = 777; //Dictates the frequency on which the "centralized server time" is updated.
public static final boolean ENABLE_PIC = false; //Pick true/false to enable or disable Pic. Delete character requires PIC available.
public static final boolean ENABLE_PIN = false; //Pick true/false to enable or disable Pin.
public static final int BYPASS_PIC_EXPIRATION = 20; //Enables PIC bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable.
public static final int BYPASS_PIN_EXPIRATION = 15; //Enables PIN bypass, which will remain active for that account by that client machine for N minutes. Set 0 to disable.
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 detection of multi-client and suspicious remote IP 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_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.
//Ip Configuration
public static String HOST;
public static boolean LOCALSERVER;
//Other Configuration
public static boolean JAVA_8;
public static boolean SHUTDOWNHOOK;
// JAVA_8: every static function in AbstractPlayerInteraction are to be made non-static, and code comment sections uncommented after enabling this functionality.
//Server Flags
public static final boolean USE_CUSTOM_KEYSET = true; //Enables auto-setup of the HeavenMS's custom keybindings when creating characters.
public static final boolean USE_DEBUG = false; //Will enable some text prints on the client, oriented for debugging purposes.
public static final boolean USE_DEBUG_SHOW_INFO_EQPEXP = false; //Prints on the cmd all equip exp gain info.
public static boolean USE_DEBUG_SHOW_RCVD_PACKET = false; //Prints on the cmd all received packet ids.
public static boolean USE_DEBUG_SHOW_RCVD_MVLIFE = false; //Prints on the cmd all received move life content.
public static final boolean USE_DEBUG_SHOW_PACKET = false;
public static boolean USE_SUPPLY_RATE_COUPONS = true; //Allows rate coupons to be sold through the Cash Shop.
public static final boolean USE_IP_VALIDATION = true; //Enables IP checking when logging in.
public static final boolean USE_MAXRANGE = true; //Will send and receive packets from all events on a map, rather than those of only view range.
public static final boolean USE_MAXRANGE_ECHO_OF_HERO = true;
public static final boolean USE_MTS = false;
public static final boolean USE_CPQ = true; //Renders the CPQ available or not.
public static final boolean USE_AUTOHIDE_GM = false; //When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152).
public static final boolean USE_BUYBACK_SYSTEM = true; //Enables the HeavenMS-builtin buyback system, dead players can use it by clicking the MTS button.
public static final boolean USE_FIXED_RATIO_HPMP_UPDATE = true; //Enables the HeavenMS-builtin HPMP update based on the current pool to max pool ratio.
public static final boolean USE_FAMILY_SYSTEM = false;
public static final boolean USE_DUEY = true;
public static final boolean USE_RANDOMIZE_HPMP_GAIN = true; //Enables randomizing on MaxHP/MaxMP gains and INT accounting for the MaxMP gain on level up.
public static final boolean USE_STORAGE_ITEM_SORT = true; //Enables storage "Arrange Items" feature.
public static final boolean USE_ITEM_SORT = true; //Enables inventory "Item Sort/Merge" feature.
public static final boolean USE_ITEM_SORT_BY_NAME = false; //Item sorting based on name rather than id.
public static final boolean USE_PARTY_FOR_STARTERS = true; //Players level 10 or below can create/invite other players on the given level range.
public static final boolean USE_AUTOASSIGN_STARTERS_AP = false; //Beginners level 10 or below have their AP autoassigned (they can't choose to levelup a stat). Set true ONLY if the localhost doesn't support AP assigning for beginners level 10 or below.
public static final boolean USE_AUTOASSIGN_SECONDARY_CAP = true;//Prevents AP autoassign from spending on secondary stats after the player class' cap (defined on the autoassign handler) has been reached.
public static final boolean USE_AUTOBAN = false; //Commands the server to detect infractors automatically.
public static final boolean USE_AUTOBAN_LOG = true; //Log autoban related messages. Still logs even with USE_AUTOBAN disabled.
public static final boolean USE_AUTOSAVE = true; //Enables server autosaving feature (saves characters to DB each 1 hour).
public static final boolean USE_SERVER_AUTOASSIGNER = true; //HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
public static final boolean USE_REFRESH_RANK_MOVE = true;
public static final boolean USE_ENFORCE_ADMIN_ACCOUNT = false; //Forces accounts having GM characters to be treated as a "GM account" by the client (localhost). Some of the GM account perks is the ability to FLY, but unable to TRADE.
public static final boolean USE_ENFORCE_NOVICE_EXPRATE = false; //Hardsets experience rate 1x for beginners level 10 or under. Ideal for roaming on novice areas without caring too much about losing some stats.
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_JOB_SP_RANGE = false; //Caps the player SP level on the total obtainable by their current jobs. After changing jobs, missing SP will be retrieved.
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 from players.
public static final boolean USE_ENFORCE_UNMERCHABLE_CASH = false;//Forces players to not sell CASH items via merchants.
public static final boolean USE_ENFORCE_UNMERCHABLE_PET = false; //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_MERCHANT_SAVE = true; //Forces automatic DB save on merchant owners, at every item movement on shop.
public static final boolean USE_ENFORCE_MDOOR_POSITION = false; //Forces mystic door to be spawned near spawnpoints.
public static final boolean USE_SPAWN_CLEAN_MDOOR = false; //Makes mystic doors to be spawned without deploy animation. This clears disconnecting issues that may happen when trying to cancel doors a couple seconds after deployment.
public static final boolean USE_SPAWN_LOOT_ON_ANIMATION = false;//Makes loot appear some time after the mob has been killed (following the mob death animation, instead of instantly).
public static final boolean USE_SPAWN_RELEVANT_LOOT = true; //Forces to only spawn loots that are collectable by the player or any of their party members.
public static final boolean USE_ERASE_PERMIT_ON_OPENSHOP = true;//Forces "shop permit" item to be consumed when player deploy his/her player shop.
public static final boolean USE_ERASE_UNTRADEABLE_DROP = true; //Forces flagged untradeable items to disappear when dropped.
public static final boolean USE_ERASE_PET_ON_EXPIRATION = false;//Forces pets to be removed from inventory when expire time comes, rather than converting it to a doll.
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. Thanks Vcoc for this suggestion.
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.
public static final boolean USE_ENABLE_CHAT_LOG = false; //Write in-game chat to log
public static final boolean USE_REBIRTH_SYSTEM = false; //Flag to enable/disable rebirth system
public static final boolean USE_MAP_OWNERSHIP_SYSTEM = true; //Flag to enable/disable map ownership system
public static final boolean USE_FISHING_SYSTEM = true; //Flag to enable/disable fishing system
public static final boolean USE_NPCS_SCRIPTABLE = true; //Flag to enable/disable serverside predefined script NPCs.
//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_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.
public static final boolean USE_ANNOUNCE_CHANGEJOB = false; //Automatic message sent to acquantainces when changing jobs.
//Cash Shop Configuration
public static final boolean USE_JOINT_CASHSHOP_INVENTORY = true;//Enables usage of a same cash shop inventory for explorers, cygnus and legends. Items from exclusive cash shop inventories won't show up on the shared inventory, though.
public static final boolean USE_CLEAR_OUTDATED_COUPONS = true; //Enables deletion of older code coupon registry from the DB, freeing so-long irrelevant data.
//Maker Configuration
public static final boolean USE_MAKER_PERMISSIVE_ATKUP = true; //Allows players to use attack-based strengthening gems on non-weapon items.
public static final boolean USE_MAKER_FEE_HEURISTICS = true; //Apply compiled values for stimulants and reagents into the Maker fee calculations (max error revolves around 50k mesos). Set false to use basic constant values instead (results are never higher than the client-side requests).
//Custom Configuration
public static final boolean USE_ENABLE_CUSTOM_NPC_SCRIPT = true;//Enables usage of custom HeavenMS NPC scripts (Agent E, Coco, etc). Will not disable Abdula (it's actually useful for the gameplay), quests or NPC shops.
public static final boolean USE_STARTER_MERGE = false; //Allows any players to use the Equipment Merge custom mechanic (as opposed to the high-level, Maker lv3 requisites).
//Commands Configuration
public static final boolean BLOCK_GENERATE_CASH_ITEM = false; //Prevents creation of cash items with the item/drop command.
public static final boolean USE_WHOLE_SERVER_RANKING = false; //Enables a ranking pool made from every character registered on the server for the "ranks" command, instead of split by worlds.
//Server Rates And Experience
public static final int EXP_RATE = 10; //NOTE: World-specific rates within "world.ini" OVERRIDES the default rates from here.
public static final int MESO_RATE = 10;
public static final int DROP_RATE = 10;
public static final int BOSS_DROP_RATE = 10; //NOTE: Boss drop rate OVERRIDES common drop rate, for bosses-only.
public static final int QUEST_RATE = 5; //Multiplier for Exp & Meso gains when completing a quest. Only available when USE_QUEST_RATE is true. Stacks with server Exp & Meso rates.
public static final int FISHING_RATE = 10; //Multiplier for success likelihood on meso thrown during fishing.
public static final int TRAVEL_RATE = 10; //Means of transportation rides/departs using 1/N of the default time.
public static final double EQUIP_EXP_RATE = 1.0; //Rate for equipment exp gain, grows linearly. Set 1.0 for default (about 100~200 same-level range mobs killed to pass equip from level 1 to 2).
public static final double PQ_BONUS_EXP_RATE = 0.5; //Rate for the PQ exp reward.
public static final byte EXP_SPLIT_LEVEL_INTERVAL = 5; //Non-contributing players must be within N level between the mob to receive EXP.
public static final byte EXP_SPLIT_LEECH_INTERVAL = 5; //Non-contributing players must be within N level between any contributing party member to receive EXP.
public static final float EXP_SPLIT_MVP_MOD = 0.2f;
public static final float EXP_SPLIT_COMMON_MOD = 0.8f;
public static final float PARTY_BONUS_EXP_RATE = 1.0f; //Rate for the party exp bonus reward.
//Miscellaneous Configuration
public static String TIMEZONE = "GMT-3";
public static boolean USE_DISPLAY_NUMBERS_WITH_COMMA = true; //Enforce comma on displayed strings (use this when USE_UNITPRICE_WITH_COMMA is active and you still want to display comma-separated values).
public static boolean USE_UNITPRICE_WITH_COMMA = true; //Set this accordingly with the layout of the unitPrices on Item.wz XML's, whether it's using commas or dots to represent fractions.
public static final byte MAX_MONITORED_BUFFSTATS = 5; //Limits accounting for "dormant" buff effects, that should take place when stronger stat buffs expires.
public static final int MAX_AP = 32767; //Max AP allotted on the auto-assigner.
public static final int MAX_EVENT_LEVELS = 8; //Event has different levels of rewarding system.
public static final long BLOCK_NPC_RACE_CONDT = (long)(0.5 * 1000); //Time the player client must wait before reopening a conversation with an NPC.
public static final long PET_LOOT_UPON_ATTACK = (long)(0.7 * 1000); //Time the pet must wait before trying to pick items up.
public static final int TOT_MOB_QUEST_REQUIREMENT = 77; //Overwrites old 999-mobs requirement for the ToT questline with new requirement value, set 0 for default.
public static final int MOB_REACTOR_REFRESH_TIME = 30 * 1000; //Overwrites refresh time for those reactors oriented to inflict damage to bosses (Ice Queen, Riche), set 0 for default.
public static final int PARTY_SEARCH_REENTRY_LIMIT = 10; //Max amount of times a party leader is allowed to persist on the Party Search before entry expiration (thus needing to manually restart the Party Search to be able to search for members).
//Dangling Items/Locks Configuration
public static final int ITEM_EXPIRE_TIME = 3 * 60 * 1000; //Time before items start disappearing. Recommended to be set up to 3 minutes.
public static final int KITE_EXPIRE_TIME = 60 * 60 * 1000; //Time before kites (cash item) disappears.
public static final int ITEM_MONITOR_TIME = 5 * 60 * 1000; //Interval between item monitoring tasks on maps, which checks for dangling (null) item objects on the map item history.
public static final int LOCK_MONITOR_TIME = 30 * 1000; //Waiting time for a lock to be released. If it reaches timeout, a critical server deadlock has made present.
//Map Monitor Configuration
public static final int ITEM_EXPIRE_CHECK = 10 * 1000; //Interval between item expiring tasks on maps, which checks and makes disappear expired items.
public static final int ITEM_LIMIT_ON_MAP = 200; //Max number of items allowed on a map.
public static final int MAP_VISITED_SIZE = 5; //Max length for last mapids a player visits. This is used to recover and update drops on these maps accordingly with player actions.
public static final int MAP_DAMAGE_OVERTIME_INTERVAL = 5000;//Interval in milliseconds between map environment damage (e.g. El Nath and Aqua Road surrondings).
//Channel Mob Disease Monitor Configuration
public static final int MOB_STATUS_MONITOR_PROC = 200; //Frequency in milliseconds between each proc on the mob disease monitor schedule.
public static final int MOB_STATUS_MONITOR_LIFE = 84; //Idle proc count the mob disease monitor is allowed to be there before closing it due to inactivity.
public static final int MOB_STATUS_AGGRO_PERSISTENCE = 2; //Idle proc count on aggro update for a mob to keep following the current controller, given him/her is the leading damage dealer.
public static final int MOB_STATUS_AGGRO_INTERVAL = 5000; //Interval in milliseconds between aggro logistics update.
//Some Gameplay Enhancing Configurations
//Scroll Configuration
public static final boolean USE_PERFECT_GM_SCROLL = true; //Scrolls from GMs never uses up slots nor fails.
public static final boolean USE_PERFECT_SCROLLING = true; //Scrolls doesn't use slots upon failure.
public static final boolean USE_ENHANCED_CHSCROLL = true; //Equips even more powerful with chaos upgrade.
public static final boolean USE_ENHANCED_CRAFTING = true; //Apply chaos scroll on every equip crafted.
public static final boolean USE_ENHANCED_CLNSLATE = true; //Clean slates can be applied to recover successfully used slots as well.
public static final int SCROLL_CHANCE_RATE = 10; //Number of rolls for success on a scroll, set 1 for default.
public static final int CHSCROLL_STAT_RATE = 3; //Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default.
public static final int CHSCROLL_STAT_RANGE = 6; //Stat upgrade range (-N, N) on chaos scrolls.
//Beginner Skills Configuration
public static final boolean USE_ULTRA_NIMBLE_FEET = true; //Massive speed & jump upgrade.
public static final boolean USE_ULTRA_RECOVERY = true; //Massive recovery amounts overtime.
public static final boolean USE_ULTRA_THREE_SNAILS = true; //Massive damage on shell toss.
//Other Skills Configuration
public static final boolean USE_FULL_ARAN_SKILLSET = false; //Enables starter availability to all Aran job skills. Thanks Masterrulax for this suggestion.
public static final boolean USE_FAST_REUSE_HERO_WILL = true;//Greatly reduce cooldown on Hero's Will.
public static final boolean USE_ANTI_IMMUNITY_CRASH = true; //Crash skills additionally removes the mob's invincibility buffs. Thanks Celestial for this suggestion.
public static final boolean USE_UNDISPEL_HOLY_SHIELD = true;//Holy shield buff also prevents players from suffering dispel from mobs.
//Character Configuration
public static final boolean USE_ADD_SLOTS_BY_LEVEL = true; //Slots are added each 20 levels.
public static final boolean USE_ADD_RATES_BY_LEVEL = true; //Rates are added each 20 levels.
public static final boolean USE_STACK_COUPON_RATES = false; //Multiple coupons effects builds up together.
public static final boolean USE_PERFECT_PITCH = true; //For lvl 30 or above, each lvlup grants player 1 perfect pitch.
//Quest Configuration
public static final boolean USE_QUEST_RATE = false; //Exp/Meso gain by quests uses fixed server exp/meso rate times quest rate as multiplier, instead of player rates.
//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_MIN_PARTNERS = 6; //Minimum number of members on Guild Headquarters to establish a new guild.
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.
public static final boolean USE_EQUIPMNT_LVLUP_POWER = true;//Enable more powerful stat upgrades at equip level up.
public static final boolean USE_EQUIPMNT_LVLUP_CASH = true; //Enable equip leveling up on cash equipments as well.
public static final boolean USE_SPIKES_AVOID_BANISH = true; //Shoes equipped with spikes prevents mobs from banishing wearer.
public static final int MAX_EQUIPMNT_LVLUP_STAT_UP = 10000; //Max stat upgrade an equipment can have on a levelup.
public static final int MAX_EQUIPMNT_STAT = 32767; //Max stat on an equipment by leveling up.
public static final int USE_EQUIPMNT_LVLUP = 7; //All equips lvlup at max level of N, set 1 to disable.
//Map-Chair Configuration
public static final boolean USE_CHAIR_EXTRAHEAL = true; //Enable map chairs to further recover player's HP and MP (player must have the Chair Mastery skill).
public static final byte CHAIR_EXTRA_HEAL_MULTIPLIER = 10; //Due to only being able to be send up-to-255 heal values, values being actually updated is the one displayed times this.
public static final int CHAIR_EXTRA_HEAL_MAX_DELAY = 21; //Players are expected to recover fully after using this skill for N seconds.
//Player NPC Configuration
public static final int PLAYERNPC_INITIAL_X = 262; //Map frame width for putting PlayerNPCs.
public static final int PLAYERNPC_INITIAL_Y = 262; //Map frame height for putting PlayerNPCs.
public static final int PLAYERNPC_AREA_X = 320; //Initial width gap between PlayerNPCs.
public static final int PLAYERNPC_AREA_Y = 160; //Initial height gap between PlayerNPCs.
public static final int PLAYERNPC_AREA_STEPS = 4; //Max number of times gap is shortened to comport PlayerNPCs.
public static final boolean PLAYERNPC_ORGANIZE_AREA = true; //Automatically rearranges PlayerNPCs on the map if there is no space set the new NPC. Current distance gap between NPCs is decreased to solve this issue.
public static final boolean PLAYERNPC_AUTODEPLOY = true; //Makes PlayerNPC automatically deployed on the Hall of Fame at the instant one reaches max level. If false, eligible players must talk to 1st job instructor to deploy a NPC.
//Pet Auto-Pot Configuration
public static final boolean USE_COMPULSORY_AUTOPOT = true; //Pets will consume as many potions as needed to fulfill the AUTOHP/MP ratio threshold.
public static final boolean USE_EQUIPS_ON_AUTOPOT = true; //Player MaxHP and MaxMP check values on autopot handler will change according to HP/MP bonuses on equipped items.
public static final double PET_AUTOHP_RATIO = 0.99; //Will automatically consume potions until given ratio of the MaxHP/MaxMP is reached.
public static final double PET_AUTOMP_RATIO = 0.99;
//Pet & Mount Configuration
public static final byte PET_EXHAUST_COUNT = 3; //Number of proc counts (1 per minute) on the exhaust schedule for fullness.
public static final byte MOUNT_EXHAUST_COUNT = 1; //Number of proc counts (1 per minute) on the exhaust schedule for tiredness.
//Pet Hunger Configuration
public static final boolean PETS_NEVER_HUNGRY = false; //If true, pets and mounts will never grow hungry.
public static final boolean GM_PETS_NEVER_HUNGRY = true; //If true, pets and mounts own by GMs will never grow hungry.
//Event Configuration
public static final int EVENT_MAX_GUILD_QUEUE = 10; //Max number of guilds in queue for GPQ.
public static final long EVENT_LOBBY_DELAY = 10; //Cooldown duration in seconds before reopening an event lobby.
//Dojo Configuration
public static final boolean USE_FAST_DOJO_UPGRADE = true; //Reduced Dojo training points amount required for a belt upgrade.
public static final boolean USE_DEADLY_DOJO = false; //Should bosses really use 1HP,1MP attacks in dojo?
public static final int DOJO_ENERGY_ATK = 100; //Dojo energy gain when deal attack
public static final int DOJO_ENERGY_DMG = 20; //Dojo energy gain when recv attack
//Wedding Configuration
public static final int WEDDING_RESERVATION_DELAY = 3; //Minimum idle slots before processing a wedding reservation.
public static final int WEDDING_RESERVATION_TIMEOUT = 10; //Limit time in minutes for the couple to show up before cancelling the wedding reservation.
public static final int WEDDING_RESERVATION_INTERVAL = 60; //Time between wedding starts in minutes.
public static final int WEDDING_BLESS_EXP = 30000; //Exp gained per bless count.
public static final int WEDDING_GIFT_LIMIT = 1; //Max number of gifts per person to same wishlist on marriage instances.
public static final boolean WEDDING_BLESSER_SHOWFX = true; //Pops bubble sprite effect on players blessing the couple. Setting this false shows the blessing effect on the couple instead.
//Buyback Configuration
public static final boolean USE_BUYBACK_WITH_MESOS = true; //Enables usage of either mesos or NX for the buyback fee.
public static final float BUYBACK_FEE = 77.70f; //Sets the base amount needed to buyback (level 30 or under will use the base value).
public static final float BUYBACK_LEVEL_STACK_FEE = 85.47f; //Sets the level-stacking portion of the amount needed to buyback (fee will sum up linearly until level 120, when it reaches the peak).
public static final int BUYBACK_MESO_MULTIPLIER = 1000; //Sets a multiplier for the fee when using meso as the charge unit.
public static final int BUYBACK_RETURN_MINUTES = 1; //Sets the maximum amount of time the player can wait before decide to buyback.
public static final int BUYBACK_COOLDOWN_MINUTES = 7; //Sets the time the player must wait before using buyback again.
//Event End Timestamp
public static final long EVENT_END_TIMESTAMP = 1428897600000L;
//Debug Variables
public static int DEBUG_VALUES[] = new int[10]; // Field designed for packet testing purposes
//Properties
static {
Properties p = new Properties();
try {
p.load(new FileInputStream("configuration.ini"));
//Server Host
ServerConstants.HOST = p.getProperty("HOST");
ServerConstants.LOCALSERVER = ServerConstants.HOST.startsWith("127.") || ServerConstants.HOST.startsWith("localhost");
//Sql Database
ServerConstants.DB_URL = p.getProperty("URL");
ServerConstants.DB_USER = p.getProperty("DB_USER");
ServerConstants.DB_PASS = p.getProperty("DB_PASS");
//java8 And Shutdownhook
ServerConstants.JAVA_8 = p.getProperty("JAVA8").equalsIgnoreCase("TRUE");
ServerConstants.SHUTDOWNHOOK = p.getProperty("SHUTDOWNHOOK").equalsIgnoreCase("true");
} catch (Exception e) {
e.printStackTrace();
System.out.println("Failed to load configuration.ini.");
System.exit(0);
}
}
}

View File

@@ -19,7 +19,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package constants;
package constants.game;
public final class ExpTable {
private static final int[] exp = {15, 15, 34, 57, 92, 135, 372, 560, 840, 1144, 1242, 1573, 2144, 2800, 3640, 4700, 5893, 7360, 9144, 11120, 13477, 16268, 19320, 22880, 27008, 31477, 36600, 42444, 48720, 55813, 63800, 86784, 98208, 110932, 124432, 139372, 155865, 173280, 192400, 213345, 235372, 259392, 285532, 312928, 342624, 374760, 408336, 445544, 483532, 524160, 567772, 598886, 631704, 666321, 702836, 741351, 781976, 824828, 870028, 917625, 967995, 1021041, 1076994, 1136013, 1198266, 1263930, 1333194, 1406252, 1483314, 1564600, 1650340, 1740778, 1836173, 1936794, 2042930, 2154882, 2272970, 2397528, 2528912, 2667496, 2813674, 2967863, 3130502, 3302053, 3483005, 3673873, 3875201, 4087562, 4311559, 4547832, 4797053, 5059931, 5337215, 5629694, 5938202, 6263614, 6606860, 6968915, 7350811, 7753635, 8178534, 8626718, 9099462, 9598112, 10124088, 10678888, 11264090, 11881362, 12532461, 13219239, 13943653, 14707765, 15513750, 16363902, 17260644, 18206527, 19204245, 20256637, 21366700, 22537594, 23772654, 25075395, 26449526, 27898960, 29427822, 31040466, 32741483, 34535716, 36428273, 38424542, 40530206, 42751262, 45094030, 47565183, 50171755, 52921167, 55821246, 58880250, 62106888, 65510344, 69100311, 72887008, 76881216, 81094306, 85594273, 90225770, 95170142, 100385466, 105886589, 111689174, 117809740, 124265714, 131075474, 138258410, 145834970, 153826726, 162256430, 171148082, 180526997, 190419876, 200854885, 211861732, 223471711, 223471711, 248635353, 262260570, 276632449, 291791906, 307782102, 324648562, 342439302, 361204976, 380999008, 401877754, 423900654, 447130410, 471633156, 497478653, 524740482, 553496261, 583827855, 615821622, 649568646, 685165008, 722712050, 762316670, 804091623, 848155844, 894634784, 943660770, 995373379, 1049919840, 1107455447, 1168144006, 1232158297, 1299680571, 1370903066, 1446028554, 1525246918, 1608855764, 1697021059};

View File

@@ -1,4 +1,4 @@
package constants;
package constants.game;
import client.MapleDisease;
import java.util.ArrayList;
@@ -6,10 +6,18 @@ import java.util.List;
import java.util.HashMap;
import java.util.Map;
import client.MapleJob;
import config.YamlConfig;
import constants.skills.Aran;
import java.io.File;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.Locale;
import provider.MapleData;
import provider.MapleDataDirectoryEntry;
import provider.MapleDataFileEntry;
import provider.MapleDataProvider;
import provider.MapleDataProviderFactory;
import provider.MapleDataTool;
import server.maps.MapleMap;
import server.maps.FieldLimit;
import server.quest.MapleQuest;
@@ -33,11 +41,13 @@ public class GameConstants {
private static final int[] jobUpgradeSpUp = {0, 1, 2, 3, 6};
private final static Map<Integer, String> jobNames = new HashMap<>();
private final static NumberFormat nfFormatter = new DecimalFormat("#,###,###,###");
private final static NumberFormat nfParser = NumberFormat.getInstance(ServerConstants.USE_UNITPRICE_WITH_COMMA ? Locale.FRANCE : Locale.UK);
private final static NumberFormat nfParser = NumberFormat.getInstance(YamlConfig.config.server.USE_UNITPRICE_WITH_COMMA ? Locale.FRANCE : Locale.UK);
public static final MapleDisease[] CPQ_DISEASES = {MapleDisease.SLOW, MapleDisease.SEDUCE, MapleDisease.STUN, MapleDisease.POISON,
MapleDisease.SEAL, MapleDisease.DARKNESS, MapleDisease.WEAKEN, MapleDisease.CURSE};
public static final int MAX_FIELD_MOB_DAMAGE = getMaxObstacleMobDamageFromWz() * 2;
public static int getPlayerBonusDropRate(int slot) {
return(DROP_RATE_GAIN[slot]);
}
@@ -480,18 +490,6 @@ public class GameConstants {
}
}
public static int getHiddenSkill(final int skill) {
switch (skill) {
case Aran.HIDDEN_FULL_DOUBLE:
case Aran.HIDDEN_FULL_TRIPLE:
return Aran.FULL_SWING;
case Aran.HIDDEN_OVER_DOUBLE:
case Aran.HIDDEN_OVER_TRIPLE:
return Aran.OVER_SWING;
}
return skill;
}
public static int getSkillBook(final int job) {
if (job >= 2210 && job <= 2218) {
return job - 2209;
@@ -563,7 +561,7 @@ public class GameConstants {
}
public static boolean isFreeMarketRoom(int mapid) {
return mapid > 910000000 && mapid < 910000023;
return mapid / 1000000 == 910 && mapid > 910000000; // FM rooms subset, thanks to shavit
}
public static boolean isMerchantLocked(MapleMap map) {
@@ -657,7 +655,7 @@ public class GameConstants {
}
public synchronized static String numberWithCommas(int i) {
if(!ServerConstants.USE_DISPLAY_NUMBERS_WITH_COMMA) {
if(!YamlConfig.config.server.USE_DISPLAY_NUMBERS_WITH_COMMA) {
return nfFormatter.format(i); // will display number on whatever locale is currently assigned on NumberFormat
} else {
return NumberFormat.getNumberInstance(Locale.UK).format(i);
@@ -672,4 +670,32 @@ public class GameConstants {
return 0.0f;
}
}
private static int getMaxObstacleMobDamageFromWz() {
MapleDataProvider mapSource = MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz"));
int maxMobDmg = 0;
MapleDataDirectoryEntry root = mapSource.getRoot();
for (MapleDataDirectoryEntry objData : root.getSubdirectories()) {
if (!objData.getName().contentEquals("Obj")) {
continue;
}
for (MapleDataFileEntry obj : objData.getFiles()) {
for (MapleData l0 : mapSource.getData(objData.getName() + "/" + obj.getName()).getChildren()) {
for (MapleData l1 : l0.getChildren()) {
for (MapleData l2 : l1.getChildren()) {
int objDmg = MapleDataTool.getIntConvert("s1/mobdamage", l2, 0);
if (maxMobDmg < objDmg) {
maxMobDmg = objDmg;
}
}
}
}
}
}
return maxMobDmg;
}
}

View File

@@ -1,4 +1,4 @@
package constants;
package constants.game;
/**
* @brief ScriptableNPCConstants
@@ -16,7 +16,8 @@ import tools.Pair;
public class ScriptableNPCConstants {
public static final Set<Pair<Integer, String>> SCRIPTABLE_NPCS = new HashSet<Pair<Integer, String>>(){{
add(new Pair<>(9200000, "Cody"));
//add(new Pair<>(9200000, "Cody"));
add(new Pair<>(9001105, "Grandpa Moon Bunny"));
}};
}

View File

@@ -1,4 +1,4 @@
package constants;
package constants.inventory;
/**
*

View File

@@ -17,7 +17,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package constants;
package constants.inventory;
import java.util.HashMap;
import java.util.Map;

View File

@@ -19,9 +19,12 @@
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 constants;
package constants.inventory;
import constants.net.ServerConstants;
import client.inventory.MapleInventoryType;
import config.YamlConfig;
import java.util.HashSet;
import java.util.Set;
import java.util.HashMap;
@@ -35,16 +38,16 @@ import java.util.Map;
public final class ItemConstants {
protected static Map<Integer, MapleInventoryType> inventoryTypeCache = new HashMap<>();
public final static int LOCK = 0x01;
public final static int SPIKES = 0x02;
public final static int KARMA_USE = 0x02;
public final static int COLD = 0x04;
public final static int UNTRADEABLE = 0x08;
public final static int KARMA_EQP = 0x10;
public final static int SANDBOX = 0x40; // let 0x40 until it's proven something uses this
public final static int PET_COME = 0x80;
public final static int ACCOUNT_SHARING = 0x100;
public final static int MERGE_UNTRADEABLE = 0x200;
public final static short LOCK = 0x01;
public final static short SPIKES = 0x02;
public final static short KARMA_USE = 0x02;
public final static short COLD = 0x04;
public final static short UNTRADEABLE = 0x08;
public final static short KARMA_EQP = 0x10;
public final static short SANDBOX = 0x40; // let 0x40 until it's proven something uses this
public final static short PET_COME = 0x80;
public final static short ACCOUNT_SHARING = 0x100;
public final static short MERGE_UNTRADEABLE = 0x200;
public final static boolean EXPIRING_ITEMS = true;
public final static Set<Integer> permanentItemids = new HashSet<>();
@@ -107,7 +110,7 @@ public final class ItemConstants {
}
public static boolean isExpirablePet(int itemId) {
return ServerConstants.USE_ERASE_PET_ON_EXPIRATION || itemId == 5000054;
return YamlConfig.config.server.USE_ERASE_PET_ON_EXPIRATION || itemId == 5000054;
}
public static boolean isPermanentItem(int itemId) {
@@ -147,7 +150,7 @@ public final class ItemConstants {
return scrollId == 2040727 || scrollId == 2041058;
}
public static boolean isFlagModifier(int scrollId, byte flag) {
public static boolean isFlagModifier(int scrollId, short flag) {
if(scrollId == 2041058 && ((flag & ItemConstants.COLD) == ItemConstants.COLD)) return true;
if(scrollId == 2040727 && ((flag & ItemConstants.SPIKES) == ItemConstants.SPIKES)) return true;
return false;
@@ -167,11 +170,11 @@ public final class ItemConstants {
}
public static boolean isPartyItem(int itemId) {
return itemId >= 2022430 && itemId <= 2022433;
return itemId >= 2022430 && itemId <= 2022433 || itemId >= 2022160 && itemId <= 2022163;
}
public static boolean isPartyAllcure(int itemId) {
return itemId == 2022433;
return itemId == 2022433 || itemId == 2022163;
}
public static boolean isHiredMerchant(int itemId) {
@@ -247,4 +250,12 @@ public final class ItemConstants {
public static boolean isHair(int itemId) {
return itemId >= 30000 && itemId < 35000;
}
public static boolean isFaceExpression(int itemId) {
return itemId / 10000 == 516;
}
public static boolean isChair(int itemId) {
return itemId / 10000 == 301;
}
}

View File

@@ -17,7 +17,7 @@
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package constants;
package constants.net;
import java.util.Map;
import java.util.HashMap;

View File

@@ -0,0 +1,35 @@
package constants.net;
public class ServerConstants {
//Server Version
public static short VERSION = 83;
//Java Configuration
public static final boolean JAVA_8 = getJavaVersion() >= 8; //Max amount of times a party leader is allowed to persist on the Party Search before entry expiration (thus needing to manually restart the Party Search to be able to search for members).
//Debug Variables
public static int DEBUG_VALUES[] = new int[10]; // Field designed for packet testing purposes
// https://github.com/openstreetmap/josm/blob/a3a6e8a6b657cf4c5b4c64ea14d6e87be6280d65/src/org/openstreetmap/josm/tools/Utils.java#L1566-L1585
/**
* Returns the Java version as an int value.
* @return the Java version as an int value (8, 9, etc.)
* @since 12130
*/
public static int getJavaVersion() {
String version = System.getProperty("java.version");
if (version.startsWith("1.")) {
version = version.substring(2);
}
// Allow these formats:
// 1.8.0_72-ea
// 9-ea
// 9
// 9.0.1
int dotPos = version.indexOf('.');
int dashPos = version.indexOf('-');
return Integer.parseInt(version.substring(0,
dotPos > -1 ? dotPos : dashPos > -1 ? dashPos : 1));
}
}

View File

@@ -29,11 +29,13 @@ public class Aran {
public static final int DOUBLE_SWING = 21000002;
public static final int TRIPLE_SWING = 21100001;
public static final int COMBO_ABILITY = 21000000;
public static final int COMBAT_STEP = 21001001;
public static final int POLEARM_BOOSTER = 21001003;
public static final int MAPLE_WARRIOR = 21121000;
public static final int FREEZE_STANDING = 21121003;
public static final int SNOW_CHARGE = 21111005;
public static final int HEROS_WILL = 21121008;
public static final int HIGH_DEFENSE = 21120004;
public static final int BODY_PRESSURE = 21101003;
public static final int COMBO_DRAIN = 21100005;
public static final int COMBO_SMASH = 21100004;

View File

@@ -29,5 +29,6 @@ public class FPWizard {
public static final int MP_EATER = 2100000;
public static final int MEDITATION = 2101001;
public static final int SLOW = 2101003;
public static final int FIRE_ARROW = 2101004;
public static final int POISON_BREATH = 2101005;
}

View File

@@ -1,31 +0,0 @@
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package constants.skills;
/**
*
* @author BubblesDev
*/
public class Swordsman {
public static final int IMPROVED_MAX_HP_INCREASE = 1000001;
public static final int IRON_BODY = 1000003;
}

View File

@@ -11,4 +11,5 @@ package constants.skills;
public class Warrior {
public static final int IMPROVED_HPREC = 1000000;
public static final int IMPROVED_MAXHP = 1000001;
public static final int IRON_BODY = 1000003;
}

View File

@@ -4,7 +4,7 @@
* and open the template in the editor.
*/
package constants;
package constants.string;
/*
* Thanks to GabrielSin (EllinMS) - gabrielsin@playellin.net

View File

@@ -1,4 +1,4 @@
package constants;
package constants.string;
import client.MapleCharacter;

View File

@@ -28,19 +28,20 @@ import java.util.HashSet;
import java.util.Calendar;
import java.util.concurrent.atomic.AtomicLong;
import config.YamlConfig;
import org.apache.mina.core.service.IoHandlerAdapter;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import client.MapleClient;
import constants.ServerConstants;
import constants.net.ServerConstants;
import java.net.InetSocketAddress;
import net.server.Server;
import net.server.audit.locks.MonitoredLockType;
import net.server.audit.locks.MonitoredReentrantLock;
import net.server.audit.locks.factory.MonitoredReentrantLockFactory;
import net.server.coordinator.MapleSessionCoordinator;
import net.server.coordinator.session.MapleSessionCoordinator;
import tools.FilePrinter;
import tools.MapleAESOFB;
@@ -188,7 +189,7 @@ public class MapleServerHandler extends IoHandlerAdapter {
short packetId = slea.readShort();
MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
if(ServerConstants.USE_DEBUG_SHOW_RCVD_PACKET && !ignoredDebugRecvPackets.contains(packetId)) System.out.println("Received packet id " + packetId);
if(YamlConfig.config.server.USE_DEBUG_SHOW_RCVD_PACKET && !ignoredDebugRecvPackets.contains(packetId)) System.out.println("Received packet id " + packetId);
final MaplePacketHandler packetHandler = processor.getHandler(packetId);
if (packetHandler != null && packetHandler.validateState(client)) {
try {
@@ -198,6 +199,7 @@ public class MapleServerHandler extends IoHandlerAdapter {
FilePrinter.printError(FilePrinter.PACKET_HANDLER + packetHandler.getClass().getName() + ".txt", t, "Error for " + (client.getPlayer() == null ? "" : "player ; " + client.getPlayer() + " on map ; " + client.getPlayer().getMapId() + " - ") + "account ; " + client.getAccountName() + "\r\n" + slea.toString());
//client.announce(MaplePacketCreator.enableActions());//bugs sometimes
}
client.updateLastPacket();
}
}
@@ -240,14 +242,14 @@ public class MapleServerHandler extends IoHandlerAdapter {
long timeNow = Server.getInstance().getCurrentTime();
long timeThen = timeNow - 15000;
Set<MapleClient> pingClients = new HashSet<>();
idleLock.lock();
try {
for(Entry<MapleClient, Long> mc : idleSessions.entrySet()) {
if(timeNow - mc.getValue() >= 15000) {
mc.getKey().testPing(timeThen);
pingClients.add(mc.getKey());
}
}
idleSessions.clear();
if(!tempIdleSessions.isEmpty()) {
@@ -265,6 +267,10 @@ public class MapleServerHandler extends IoHandlerAdapter {
} finally {
idleLock.unlock();
}
for(MapleClient c : pingClients) {
c.testPing(timeThen);
}
}
private void idleManagerTask() {

View File

@@ -163,6 +163,7 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.CANCEL_BUFF, new CancelBuffHandler());
registerHandler(RecvOpcode.CANCEL_ITEM_EFFECT, new CancelItemEffectHandler());
registerHandler(RecvOpcode.PLAYER_INTERACTION, new PlayerInteractionHandler());
registerHandler(RecvOpcode.RPS_ACTION, new RPSActionHandler());
registerHandler(RecvOpcode.DISTRIBUTE_AP, new DistributeAPHandler());
registerHandler(RecvOpcode.DISTRIBUTE_SP, new DistributeSPHandler());
registerHandler(RecvOpcode.CHANGE_KEYMAP, new KeymapChangeHandler());
@@ -222,8 +223,14 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.MONSTER_BOOK_COVER, new MonsterBookCoverHandler());
registerHandler(RecvOpcode.AUTO_DISTRIBUTE_AP, new AutoAssignHandler());
registerHandler(RecvOpcode.MAKER_SKILL, new MakerSkillHandler());
registerHandler(RecvOpcode.OPEN_FAMILY_PEDIGREE, new OpenFamilyPedigreeHandler());
registerHandler(RecvOpcode.OPEN_FAMILY, new OpenFamilyHandler());
registerHandler(RecvOpcode.ADD_FAMILY, new FamilyAddHandler());
registerHandler(RecvOpcode.SEPARATE_FAMILY_BY_SENIOR, new FamilySeparateHandler());
registerHandler(RecvOpcode.SEPARATE_FAMILY_BY_JUNIOR, new FamilySeparateHandler());
registerHandler(RecvOpcode.USE_FAMILY, new FamilyUseHandler());
registerHandler(RecvOpcode.CHANGE_FAMILY_MESSAGE, new FamilyPreceptsHandler());
registerHandler(RecvOpcode.FAMILY_SUMMON_RESPONSE, new FamilySummonResponseHandler());
registerHandler(RecvOpcode.USE_HAMMER, new UseHammerHandler());
registerHandler(RecvOpcode.SCRIPTED_ITEM, new ScriptedItemHandler());
registerHandler(RecvOpcode.TOUCHING_REACTOR, new TouchReactorHandler());
@@ -244,6 +251,7 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.PLAYER_MAP_TRANSFER, new PlayerMapTransitionHandler());
registerHandler(RecvOpcode.USE_MAPLELIFE, new UseMapleLifeHandler());
registerHandler(RecvOpcode.USE_CATCH_ITEM, new UseCatchItemHandler());
registerHandler(RecvOpcode.FIELD_DAMAGE_MOB, new FieldDamageMobHandler());
registerHandler(RecvOpcode.MOB_DAMAGE_MOB_FRIENDLY, new MobDamageMobFriendlyHandler());
registerHandler(RecvOpcode.PARTY_SEARCH_REGISTER, new PartySearchRegisterHandler());
registerHandler(RecvOpcode.PARTY_SEARCH_START, new PartySearchStartHandler());

View File

@@ -21,10 +21,10 @@
*/
package net.mina;
import constants.ServerConstants;
import config.YamlConfig;
import client.MapleClient;
import constants.OpcodeConstants;
import net.server.coordinator.MapleSessionCoordinator;
import constants.net.OpcodeConstants;
import net.server.coordinator.session.MapleSessionCoordinator;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.CumulativeProtocolDecoder;
@@ -33,7 +33,6 @@ import tools.HexTool;
import tools.MapleAESOFB;
import tools.data.input.ByteArrayByteStream;
import tools.data.input.GenericLittleEndianAccessor;
import net.opcodes.RecvOpcode;
import tools.FilePrinter;
public class MaplePacketDecoder extends CumulativeProtocolDecoder {
@@ -75,7 +74,7 @@ public class MaplePacketDecoder extends CumulativeProtocolDecoder {
rcvdCrypto.crypt(decryptedPacket);
MapleCustomEncryption.decryptData(decryptedPacket);
out.write(decryptedPacket);
if (ServerConstants.USE_DEBUG_SHOW_PACKET){ // Atoot's idea: packet traffic log, applied using auto-identation thanks to lrenex
if (YamlConfig.config.server.USE_DEBUG_SHOW_PACKET){ // packet traffic log: Atoot's idea, applied using auto-identation thanks to lrenex
int packetLen = decryptedPacket.length;
int pHeader = readFirstShort(decryptedPacket);
String pHeaderStr = Integer.toHexString(pHeader).toUpperCase();

View File

@@ -21,11 +21,10 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.mina;
import constants.ServerConstants;
import config.YamlConfig;
import client.MapleClient;
import constants.OpcodeConstants;
import net.opcodes.SendOpcode;
import net.server.coordinator.MapleSessionCoordinator;
import constants.net.OpcodeConstants;
import net.server.coordinator.session.MapleSessionCoordinator;
import org.apache.mina.core.buffer.IoBuffer;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.codec.ProtocolEncoder;
@@ -43,40 +42,41 @@ public class MaplePacketEncoder implements ProtocolEncoder {
final MapleClient client = (MapleClient) session.getAttribute(MapleClient.CLIENT_KEY);
try {
client.lockEncoder();
try {
final MapleAESOFB send_crypto = client.getSendCrypto();
final byte[] input = (byte[]) message;
if (ServerConstants.USE_DEBUG_SHOW_PACKET) {
int packetLen = input.length;
int pHeader = readFirstShort(input);
String pHeaderStr = Integer.toHexString(pHeader).toUpperCase();
String op = lookupRecv(pHeader);
String Recv = "ServerSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n";
if (packetLen <= 50000) {
String RecvTo = Recv + HexTool.toString(input) + "\r\n" + HexTool.toStringFromAscii(input);
System.out.println(RecvTo);
if (op == null) {
System.out.println("UnknownPacket:" + RecvTo);
if (client.tryacquireEncoder()) {
try {
final MapleAESOFB send_crypto = client.getSendCrypto();
final byte[] input = (byte[]) message;
if (YamlConfig.config.server.USE_DEBUG_SHOW_PACKET) {
int packetLen = input.length;
int pHeader = readFirstShort(input);
String pHeaderStr = Integer.toHexString(pHeader).toUpperCase();
String op = lookupRecv(pHeader);
String Recv = "ServerSend:" + op + " [" + pHeaderStr + "] (" + packetLen + ")\r\n";
if (packetLen <= 50000) {
String RecvTo = Recv + HexTool.toString(input) + "\r\n" + HexTool.toStringFromAscii(input);
System.out.println(RecvTo);
if (op == null) {
System.out.println("UnknownPacket:" + RecvTo);
}
} else {
FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ...");
}
} else {
FilePrinter.print(FilePrinter.PACKET_STREAM + MapleSessionCoordinator.getSessionRemoteAddress(session) + ".txt", HexTool.toString(new byte[]{input[0], input[1]}) + " ...");
}
final byte[] unencrypted = new byte[input.length];
System.arraycopy(input, 0, unencrypted, 0, input.length);
final byte[] ret = new byte[unencrypted.length + 4];
final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
MapleCustomEncryption.encryptData(unencrypted);
send_crypto.crypt(unencrypted);
System.arraycopy(header, 0, ret, 0, 4);
System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
out.write(IoBuffer.wrap(ret));
} finally {
client.unlockEncoder();
}
final byte[] unencrypted = new byte[input.length];
System.arraycopy(input, 0, unencrypted, 0, input.length);
final byte[] ret = new byte[unencrypted.length + 4];
final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
MapleCustomEncryption.encryptData(unencrypted);
send_crypto.crypt(unencrypted);
System.arraycopy(header, 0, ret, 0, 4);
System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
out.write(IoBuffer.wrap(ret));
} finally {
client.unlockEncoder();
}
// System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
// out.write(ByteBuffer.wrap(ret));

View File

@@ -145,10 +145,15 @@ public enum RecvOpcode {
WEDDING_TALK_MORE(0x8B),
ALLIANCE_OPERATION(0x8F),
DENY_ALLIANCE_REQUEST(0x90),
OPEN_FAMILY_PEDIGREE(0x91),
OPEN_FAMILY(0x92),
ADD_FAMILY(0x93),
SEPARATE_FAMILY_BY_SENIOR(0x94),
SEPARATE_FAMILY_BY_JUNIOR(0x95),
ACCEPT_FAMILY(0x96),
USE_FAMILY(0x97),
CHANGE_FAMILY_MESSAGE(0x98),
FAMILY_SUMMON_RESPONSE(0x99),
BBS_OPERATION(0x9B),
ENTER_MTS(0x9C),
USE_SOLOMON_ITEM(0x9D),
@@ -170,6 +175,7 @@ public enum RecvOpcode {
MOVE_DRAGON(0xB5),
MOVE_LIFE(0xBC),
AUTO_AGGRO(0xBD),
FIELD_DAMAGE_MOB(0xBF),
MOB_DAMAGE_MOB_FRIENDLY(0xC0),
MONSTER_BOMB(0xC1),
MOB_DAMAGE_MOB(0xC2),

View File

@@ -124,7 +124,7 @@ public enum SendOpcode {
FAMILY_JOIN_REQUEST_RESULT(0x62),
FAMILY_JOIN_ACCEPTED(0x63),
FAMILY_PRIVILEGE_LIST(0x64),
FAMILY_FAMOUS_POINT_INC_RESULT(0x65),
FAMILY_REP_GAIN(0x65),
FAMILY_NOTIFY_LOGIN_OR_LOGOUT(0x66), //? is logged in. LOLWUT
FAMILY_SET_PRIVILEGE(0x67),
FAMILY_SUMMON_REQUEST(0x68),
@@ -182,7 +182,7 @@ public enum SendOpcode {
CONTI_STATE(0x95),
SET_QUEST_CLEAR(0x96),
SET_QUEST_TIME(0x97),
WARN_MESSAGE(0x98),
ARIANT_RESULT(0x98), // thanks lrenex
SET_OBJECT_STATE(0x99),
STOP_CLOCK(0x9A),
ARIANT_ARENA_SHOW_RESULT(0x9B),
@@ -239,6 +239,7 @@ public enum SendOpcode {
MESO_BAG_MESSAGE(0xD2),
UPDATE_QUEST_INFO(0xD3),
PLAYER_HINT(0xD6),
MAKER_RESULT(0xD9),
KOREAN_EVENT(0xDB),
OPEN_UI(0xDC),
LOCK_UI(0xDD),
@@ -301,7 +302,7 @@ public enum SendOpcode {
ARIANT_ARENA_USER_SCORE(0x129),
SHEEP_RANCH_INFO(0x12B),
SHEEP_RANCH_CLOTHES(0x12C),
ARIANT_SCORE(0x12D),
WITCH_TOWER_SCORE_UPDATE(0x12D), // thanks lrenex
HORNTAIL_CAVE(0x12E),
ZAKUM_SHRINE(0x12F),
NPC_TALK(0x130),

Some files were not shown because too many files have changed in this diff Show More