@@ -193,7 +193,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private int energybar;
|
||||
private int gmLevel;
|
||||
private int ci = 0;
|
||||
private MapleFamily family;
|
||||
private MapleFamilyEntry familyEntry;
|
||||
private int familyId;
|
||||
private int bookCover;
|
||||
private int battleshipHp = 0;
|
||||
@@ -331,6 +331,7 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
private long banishTime = 0;
|
||||
private long lastExpGainTime;
|
||||
private boolean pendingNameChange; //only used to change name on logout, not to be relied upon elsewhere
|
||||
private long loginTime;
|
||||
|
||||
private MapleCharacter() {
|
||||
super.setListener(new AbstractCharacterListener() {
|
||||
@@ -1254,6 +1255,10 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
if (this.guildid > 0) {
|
||||
getGuild().broadcast(MaplePacketCreator.jobMessage(0, job.getId(), name), this.getId());
|
||||
}
|
||||
MapleFamily family = getFamily();
|
||||
if(family != null) {
|
||||
family.broadcast(MaplePacketCreator.jobMessage(1, job.getId(), name), this.getId());
|
||||
}
|
||||
setMasteries(this.job.getId());
|
||||
guildUpdate();
|
||||
|
||||
@@ -1279,9 +1284,9 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
public void broadcastAcquaintances(byte[] packet) {
|
||||
buddylist.broadcast(packet, getWorldServer().getPlayerStorage());
|
||||
|
||||
MapleFamily family = getFamily();
|
||||
if(family != null) {
|
||||
//family.broadcast(packet, id); not yet implemented
|
||||
family.broadcast(packet, id);
|
||||
}
|
||||
|
||||
MapleGuild guild = getGuild();
|
||||
@@ -4873,11 +4878,17 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
}
|
||||
|
||||
public MapleFamily getFamily() {
|
||||
return family;
|
||||
if(familyEntry != null) return familyEntry.getFamily();
|
||||
else return null;
|
||||
}
|
||||
|
||||
public void setFamily(MapleFamily f) {
|
||||
this.family = f;
|
||||
|
||||
public MapleFamilyEntry getFamilyEntry() {
|
||||
return familyEntry;
|
||||
}
|
||||
|
||||
public void setFamilyEntry(MapleFamilyEntry entry) {
|
||||
if(entry != null) setFamilyId(entry.getFamily().getID());
|
||||
this.familyEntry = entry;
|
||||
}
|
||||
|
||||
public int getFamilyId() {
|
||||
@@ -6440,6 +6451,16 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
|
||||
levelUpMessages();
|
||||
guildUpdate();
|
||||
|
||||
MapleFamilyEntry familyEntry = getFamilyEntry();
|
||||
if(familyEntry != null) {
|
||||
familyEntry.giveReputationToSenior(ServerConstants.FAMILY_REP_PER_LEVELUP, true);
|
||||
MapleFamilyEntry senior = familyEntry.getSenior();
|
||||
if(senior != null) { //only send the message to direct senior
|
||||
MapleCharacter seniorChr = senior.getChr();
|
||||
if(seniorChr != null) seniorChr.announce(MaplePacketCreator.levelUpMessage(1, level, getName()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean leaveParty() {
|
||||
@@ -8555,6 +8576,20 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
psf.close();
|
||||
ps.close();
|
||||
|
||||
MapleFamilyEntry familyEntry = getFamilyEntry(); //save family rep
|
||||
if(familyEntry != null) {
|
||||
if(familyEntry.saveReputation(con)) familyEntry.savedSuccessfully();
|
||||
MapleFamilyEntry senior = familyEntry.getSenior();
|
||||
if(senior != null && senior.getChr() == null) { //only save for offline family members
|
||||
if(senior.saveReputation(con)) senior.savedSuccessfully();
|
||||
senior = senior.getSenior(); //save one level up as well
|
||||
if(senior != null && senior.getChr() == null) {
|
||||
if(senior.saveReputation(con)) senior.savedSuccessfully();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
con.commit();
|
||||
con.setAutoCommit(true);
|
||||
|
||||
@@ -10327,7 +10362,11 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
mpc = null;
|
||||
mgc = null;
|
||||
party = null;
|
||||
family = null;
|
||||
MapleFamilyEntry familyEntry = getFamilyEntry();
|
||||
if(familyEntry != null) {
|
||||
familyEntry.setCharacter(null);
|
||||
setFamilyEntry(null);
|
||||
}
|
||||
|
||||
getWorldServer().registerTimedMapObject(new Runnable() {
|
||||
@Override
|
||||
@@ -10351,6 +10390,18 @@ public class MapleCharacter extends AbstractMapleCharacterObject {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void setLoginTime(long time) {
|
||||
this.loginTime = time;
|
||||
}
|
||||
|
||||
public long getLoginTime() {
|
||||
return loginTime;
|
||||
}
|
||||
|
||||
public long getLoggedInTime() {
|
||||
return System.currentTimeMillis() - loginTime;
|
||||
}
|
||||
|
||||
public boolean isLoggedin() {
|
||||
return loggedIn;
|
||||
|
||||
@@ -25,75 +25,273 @@ 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)");
|
||||
}
|
||||
} catch(SQLException e) {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not load character information of " + cid + " in loadAllFamilies(). (SQL ERROR)");
|
||||
}
|
||||
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 FamilyDailyResetWorker
|
||||
MapleFamily family = Server.getInstance().getWorld(world).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 worker, 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
41
src/client/MapleFamilyEntitlement.java
Normal file
41
src/client/MapleFamilyEntitlement.java
Normal 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;
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ public class ServerConstants {
|
||||
public static final boolean USE_AUTOHIDE_GM = false; //When enabled, GMs are automatically hidden when joining. Thanks to Steven Deblois (steven1152).
|
||||
public static final boolean USE_BUYBACK_SYSTEM = true; //Enables the HeavenMS-builtin buyback system, to be used by dead players when clicking the MTS button.
|
||||
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_FAMILY_SYSTEM = true;
|
||||
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.
|
||||
@@ -239,6 +239,12 @@ public class ServerConstants {
|
||||
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;
|
||||
|
||||
//Family Configuration
|
||||
public static final int FAMILY_REP_PER_KILL = 4; //Amount of rep gained per monster kill.
|
||||
public static final int FAMILY_REP_PER_BOSS_KILL = 20; //Amount of rep gained per boss kill.
|
||||
public static final int FAMILY_REP_PER_LEVELUP = 200; //Amount of rep gained upon leveling up.
|
||||
public static final int FAMILY_MAX_GENERATIONS = 1000; //Maximum depth of family tree. (Distance from leader to farthest junior)
|
||||
|
||||
//Equipment Configuration
|
||||
public static final boolean USE_EQUIPMNT_LVLUP_SLOTS = true;//Equips can upgrade slots at level up.
|
||||
|
||||
@@ -223,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());
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -80,6 +80,7 @@ import org.apache.mina.filter.codec.ProtocolCodecFilter;
|
||||
import org.apache.mina.transport.socket.nio.NioSocketAcceptor;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleFamily;
|
||||
import client.MapleCharacter;
|
||||
import client.SkillFactory;
|
||||
import client.command.CommandsExecutor;
|
||||
@@ -531,7 +532,7 @@ public class Server {
|
||||
return Math.max(0, nextHour.getTimeInMillis() - System.currentTimeMillis());
|
||||
}
|
||||
|
||||
private static long getTimeLeftForNextDay() {
|
||||
public static long getTimeLeftForNextDay() {
|
||||
Calendar nextDay = Calendar.getInstance();
|
||||
nextDay.add(Calendar.DAY_OF_MONTH, 1);
|
||||
nextDay.set(Calendar.HOUR, 0);
|
||||
@@ -947,6 +948,12 @@ public class Server {
|
||||
System.out.println("[SEVERE] Syntax error in 'world.ini'.");
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
if(ServerConstants.USE_FAMILY_SYSTEM) {
|
||||
timeToTake = System.currentTimeMillis();
|
||||
MapleFamily.loadAllFamilies();
|
||||
System.out.println("Families loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n");
|
||||
}
|
||||
|
||||
acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30);
|
||||
acceptor.setHandler(new MapleServerHandler());
|
||||
|
||||
@@ -22,30 +22,132 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import constants.ServerConstants;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.MapleFamily;
|
||||
import client.MapleFamilyEntry;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.MapleInviteCoordinator.MapleInviteResult;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jay Estrella
|
||||
* @author Ubaware
|
||||
*/
|
||||
public final class AcceptFamilyHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if (!ServerConstants.USE_FAMILY_SYSTEM){
|
||||
return;
|
||||
}
|
||||
//System.out.println(slea.toString());
|
||||
if(!ServerConstants.USE_FAMILY_SYSTEM) {
|
||||
return;
|
||||
}
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
int inviterId = slea.readInt();
|
||||
//String inviterName = slea.readMapleAsciiString();
|
||||
slea.readMapleAsciiString();
|
||||
boolean accept = slea.readByte() != 0;
|
||||
// String inviterName = slea.readMapleAsciiString();
|
||||
MapleCharacter inviter = c.getWorldServer().getPlayerStorage().getCharacterById(inviterId);
|
||||
if (inviter != null) {
|
||||
inviter.getClient().announce(MaplePacketCreator.sendFamilyJoinResponse(true, c.getPlayer().getName()));
|
||||
if(inviter != null) {
|
||||
MapleInviteResult inviteResult = MapleInviteCoordinator.answerInvite(InviteType.FAMILY, c.getPlayer().getId(), c.getPlayer(), accept);
|
||||
if(inviteResult.result == InviteResult.NOT_FOUND) return; //was never invited. (or expired on server only somehow?)
|
||||
if(accept) {
|
||||
if(inviter.getFamily() != null) {
|
||||
if(chr.getFamily() == null) {
|
||||
MapleFamilyEntry newEntry = new MapleFamilyEntry(inviter.getFamily(), chr.getId(), chr.getName(), chr.getLevel(), chr.getJob());
|
||||
newEntry.setCharacter(chr);
|
||||
if(!newEntry.setSenior(inviter.getFamilyEntry(), true)) {
|
||||
inviter.announce(MaplePacketCreator.sendFamilyMessage(1, 0));
|
||||
return;
|
||||
} else {
|
||||
// save
|
||||
inviter.getFamily().addEntry(newEntry);
|
||||
insertNewFamilyRecord(chr.getId(), inviter.getFamily().getID(), inviter.getId(), false);
|
||||
}
|
||||
} else { //absorb target family
|
||||
MapleFamilyEntry targetEntry = chr.getFamilyEntry();
|
||||
MapleFamily targetFamily = targetEntry.getFamily();
|
||||
if(targetFamily.getLeader() != targetEntry) return;
|
||||
if(inviter.getFamily().getTotalGenerations() + targetFamily.getTotalGenerations() <= ServerConstants.FAMILY_MAX_GENERATIONS) {
|
||||
targetEntry.join(inviter.getFamilyEntry());
|
||||
} else {
|
||||
inviter.announce(MaplePacketCreator.sendFamilyMessage(76, 0));
|
||||
chr.announce(MaplePacketCreator.sendFamilyMessage(76, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
} else { // create new family
|
||||
if(chr.getFamily() != null && inviter.getFamily() != null && chr.getFamily().getTotalGenerations() + inviter.getFamily().getTotalGenerations() >= ServerConstants.FAMILY_MAX_GENERATIONS) {
|
||||
inviter.announce(MaplePacketCreator.sendFamilyMessage(76, 0));
|
||||
chr.announce(MaplePacketCreator.sendFamilyMessage(76, 0));
|
||||
return;
|
||||
}
|
||||
MapleFamily newFamily = new MapleFamily(-1, c.getWorld());
|
||||
c.getWorldServer().addFamily(newFamily.getID(), newFamily);
|
||||
MapleFamilyEntry inviterEntry = new MapleFamilyEntry(newFamily, inviter.getId(), inviter.getName(), inviter.getLevel(), inviter.getJob());
|
||||
inviterEntry.setCharacter(inviter);
|
||||
newFamily.setLeader(inviter.getFamilyEntry());
|
||||
newFamily.addEntry(inviterEntry);
|
||||
if(chr.getFamily() == null) { //completely new family
|
||||
MapleFamilyEntry newEntry = new MapleFamilyEntry(newFamily, chr.getId(), chr.getName(), chr.getLevel(), chr.getJob());
|
||||
newEntry.setCharacter(chr);
|
||||
newEntry.setSenior(inviterEntry, true);
|
||||
// save new family
|
||||
insertNewFamilyRecord(inviter.getId(), newFamily.getID(), 0, true);
|
||||
insertNewFamilyRecord(chr.getId(), newFamily.getID(), inviter.getId(), false); // char was already saved by setSenior() above
|
||||
newFamily.setMessage("", true);
|
||||
} else { //new family for inviter, absorb invitee family
|
||||
insertNewFamilyRecord(inviter.getId(), newFamily.getID(), 0 , true);
|
||||
newFamily.setMessage("", true);
|
||||
chr.getFamilyEntry().join(inviterEntry);
|
||||
}
|
||||
}
|
||||
c.getPlayer().getFamily().broadcast(MaplePacketCreator.sendFamilyJoinResponse(true, c.getPlayer().getName()), c.getPlayer().getId());
|
||||
c.announce(MaplePacketCreator.getSeniorMessage(inviter.getName()));
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(chr.getFamilyEntry()));
|
||||
chr.getFamilyEntry().updateSeniorFamilyInfo(true);
|
||||
} else {
|
||||
inviter.announce(MaplePacketCreator.sendFamilyJoinResponse(false, c.getPlayer().getName()));
|
||||
}
|
||||
}
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(0, 0));
|
||||
}
|
||||
|
||||
private static void insertNewFamilyRecord(int characterID, int familyID, int seniorID, boolean updateChar) {
|
||||
try(Connection con = DatabaseConnection.getConnection()) {
|
||||
try(PreparedStatement ps = con.prepareStatement("INSERT INTO family_character (cid, familyid, seniorid) VALUES (?, ?, ?)")) {
|
||||
ps.setInt(1, characterID);
|
||||
ps.setInt(2, familyID);
|
||||
ps.setInt(3, seniorID);
|
||||
ps.executeUpdate();
|
||||
} catch(SQLException e) {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not save new family record for char id " + characterID + ".");
|
||||
e.printStackTrace();
|
||||
}
|
||||
if(updateChar) {
|
||||
try(PreparedStatement ps = con.prepareStatement("UPDATE characters SET familyid = ? WHERE id = ?")) {
|
||||
ps.setInt(1, familyID);
|
||||
ps.setInt(2, characterID);
|
||||
ps.executeUpdate();
|
||||
} catch(SQLException e) {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not update 'characters' 'familyid' record for char id " + characterID + ".");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
} catch(SQLException e) {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,7 +41,7 @@ public final class DenyPartyRequestHandler extends AbstractMaplePacketHandler {
|
||||
if (cfrom != null) {
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
|
||||
if (MapleInviteCoordinator.answerInvite(InviteType.PARTY, chr.getId(), cfrom.getPartyId(), false).getLeft() == InviteResult.DENIED) {
|
||||
if (MapleInviteCoordinator.answerInvite(InviteType.PARTY, chr.getId(), cfrom.getPartyId(), false).result == InviteResult.DENIED) {
|
||||
chr.updatePartySearchAvailability(chr.getParty() == null);
|
||||
cfrom.getClient().announce(MaplePacketCreator.partyStatusMessage(23, chr.getName()));
|
||||
}
|
||||
|
||||
@@ -25,29 +25,44 @@ import constants.ServerConstants;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Jay Estrella
|
||||
* @author Ubaware
|
||||
*/
|
||||
public final class FamilyAddHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if (!ServerConstants.USE_FAMILY_SYSTEM){
|
||||
return;
|
||||
}
|
||||
System.out.println(slea.toString());
|
||||
if(!ServerConstants.USE_FAMILY_SYSTEM) {
|
||||
return;
|
||||
}
|
||||
String toAdd = slea.readMapleAsciiString();
|
||||
MapleCharacter addChr = c.getChannelServer().getPlayerStorage().getCharacterByName(toAdd);
|
||||
if (addChr != null) {
|
||||
addChr.getClient().announce(MaplePacketCreator.sendFamilyInvite(c.getPlayer().getId(), toAdd));
|
||||
c.getPlayer().dropMessage("The invite has been sent.");
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
if(addChr == null) {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(65, 0));
|
||||
} else if(addChr.getMap() != chr.getMap() || (addChr.isHidden()) && chr.gmLevel() < addChr.gmLevel()) {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(69, 0));
|
||||
} else if(addChr.getLevel() <= 10) {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(77, 0));
|
||||
} else if(Math.abs(addChr.getLevel() - chr.getLevel()) > 20) {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(72, 0));
|
||||
} else if(addChr.getFamily() != null && addChr.getFamily() == chr.getFamily()) { //same family
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
} else if(MapleInviteCoordinator.hasInvite(InviteType.FAMILY, addChr.getId())) {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(73, 0));
|
||||
} else if(chr.getFamily() != null && addChr.getFamily() != null && addChr.getFamily().getTotalGenerations() + chr.getFamily().getTotalGenerations() > ServerConstants.FAMILY_MAX_GENERATIONS) {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(76, 0));
|
||||
} else {
|
||||
c.getPlayer().dropMessage("The player cannot be found!");
|
||||
MapleInviteCoordinator.createInvite(InviteType.FAMILY, chr, addChr, addChr.getId());
|
||||
addChr.getClient().announce(MaplePacketCreator.sendFamilyInvite(chr.getId(), chr.getName()));
|
||||
chr.dropMessage("The invite has been sent.");
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
23
src/net/server/channel/handlers/FamilyPreceptsHandler.java
Normal file
23
src/net/server/channel/handlers/FamilyPreceptsHandler.java
Normal file
@@ -0,0 +1,23 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleFamily;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public class FamilyPreceptsHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
MapleFamily family = c.getPlayer().getFamily();
|
||||
if(family == null) return;
|
||||
if(family.getLeader().getChr() != c.getPlayer()) return; //only the leader can set the precepts
|
||||
String newPrecepts = slea.readMapleAsciiString();
|
||||
if(newPrecepts.length() > 200) return;
|
||||
family.setMessage(newPrecepts, true);
|
||||
//family.broadcastFamilyInfoUpdate(); //probably don't need to broadcast for this?
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(c.getPlayer().getFamilyEntry()));
|
||||
}
|
||||
|
||||
}
|
||||
78
src/net/server/channel/handlers/FamilySeparateHandler.java
Normal file
78
src/net/server/channel/handlers/FamilySeparateHandler.java
Normal file
@@ -0,0 +1,78 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleClient;
|
||||
import client.MapleFamily;
|
||||
import client.MapleFamilyEntry;
|
||||
import constants.ServerConstants;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public class FamilySeparateHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if(!ServerConstants.USE_FAMILY_SYSTEM) return;
|
||||
MapleFamily oldFamily = c.getPlayer().getFamily();
|
||||
if(oldFamily == null) return;
|
||||
MapleFamilyEntry forkOn = null;
|
||||
boolean isSenior;
|
||||
if(slea.available() > 0) { //packet 0x95 doesn't send id, since there is only one senior
|
||||
forkOn = c.getPlayer().getFamily().getEntryByID(slea.readInt());
|
||||
if(!c.getPlayer().getFamilyEntry().isJunior(forkOn)) return; //packet editing?
|
||||
isSenior = true;
|
||||
} else {
|
||||
forkOn = c.getPlayer().getFamilyEntry();
|
||||
isSenior = false;
|
||||
}
|
||||
if(forkOn == null) return;
|
||||
|
||||
MapleFamilyEntry senior = forkOn.getSenior();
|
||||
if(senior == null) return;
|
||||
int levelDiff = Math.abs(c.getPlayer().getLevel() - senior.getLevel());
|
||||
int cost = 2500 * levelDiff;
|
||||
cost += levelDiff * levelDiff;
|
||||
if(c.getPlayer().getMeso() < cost) {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(isSenior ? 81 : 80, cost));
|
||||
return;
|
||||
}
|
||||
c.getPlayer().gainMeso(-cost);
|
||||
int repCost = separateRepCost(forkOn);
|
||||
senior.gainReputation(-repCost, false);
|
||||
if(senior.getSenior() != null) senior.getSenior().gainReputation(-(repCost/2), false);
|
||||
forkOn.announceToSenior(MaplePacketCreator.serverNotice(5, forkOn.getName() + " has left the family."), true);
|
||||
forkOn.fork();
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(forkOn)); //pedigree info will be requested by the client if the window is open
|
||||
forkOn.updateSeniorFamilyInfo(true);
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(1, 0));
|
||||
}
|
||||
|
||||
|
||||
private static int separateRepCost(MapleFamilyEntry junior) {
|
||||
int level = junior.getLevel();
|
||||
int ret = level / 20;
|
||||
ret += 10;
|
||||
ret *= level;
|
||||
ret *= 2;
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.MapleFamilyEntitlement;
|
||||
import client.MapleFamilyEntry;
|
||||
import constants.ServerConstants;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.MapleInviteCoordinator.MapleInviteResult;
|
||||
import server.maps.MapleMap;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public class FamilySummonResponseHandler extends AbstractMaplePacketHandler {
|
||||
|
||||
@Override
|
||||
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if(!ServerConstants.USE_FAMILY_SYSTEM) return;
|
||||
slea.readMapleAsciiString(); //family name
|
||||
boolean accept = slea.readByte() != 0;
|
||||
MapleInviteResult inviteResult = MapleInviteCoordinator.answerInvite(InviteType.FAMILY_SUMMON, c.getPlayer().getId(), c.getPlayer(), accept);
|
||||
if(inviteResult.result == InviteResult.NOT_FOUND) return;
|
||||
MapleCharacter inviter = inviteResult.from;
|
||||
MapleFamilyEntry inviterEntry = inviter.getFamilyEntry();
|
||||
if(inviterEntry == null) return;
|
||||
MapleMap map = (MapleMap) inviteResult.params[0];
|
||||
if(accept && inviter.getMap() == map) { //cancel if inviter has changed maps
|
||||
c.getPlayer().changeMap(map, map.getPortal(0));
|
||||
} else {
|
||||
inviterEntry.refundEntitlement(MapleFamilyEntitlement.SUMMON_FAMILY);
|
||||
inviterEntry.gainReputation(MapleFamilyEntitlement.SUMMON_FAMILY.getRepCost(), false); //refund rep cost if declined
|
||||
inviter.announce(MaplePacketCreator.getFamilyInfo(inviterEntry));
|
||||
inviter.dropMessage(5, c.getPlayer().getName() + " has denied the summon request.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -21,82 +21,121 @@
|
||||
*/
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.MapleFamilyEntitlement;
|
||||
import client.MapleFamilyEntry;
|
||||
import constants.ServerConstants;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import net.opcodes.SendOpcode;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import server.maps.FieldLimit;
|
||||
import server.maps.MapleMap;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
import tools.data.output.MaplePacketLittleEndianWriter;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Moogra
|
||||
* @author Ubaware
|
||||
*/
|
||||
public final class FamilyUseHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if (!ServerConstants.USE_FAMILY_SYSTEM){
|
||||
return;
|
||||
}
|
||||
int[] repCost = {3, 5, 7, 8, 10, 12, 15, 20, 25, 40, 50};
|
||||
final int type = slea.readInt();
|
||||
if(!ServerConstants.USE_FAMILY_SYSTEM) {
|
||||
return;
|
||||
}
|
||||
MapleFamilyEntitlement type = MapleFamilyEntitlement.values()[slea.readInt()];
|
||||
int cost = type.getRepCost();
|
||||
MapleFamilyEntry entry = c.getPlayer().getFamilyEntry();
|
||||
if(entry.getReputation() < cost || entry.isEntitlementUsed(type)) {
|
||||
return; // shouldn't even be able to request it
|
||||
}
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(entry));
|
||||
MapleCharacter victim;
|
||||
if (type == 0 || type == 1) {
|
||||
if(type == MapleFamilyEntitlement.FAMILY_REUINION || type == MapleFamilyEntitlement.SUMMON_FAMILY) {
|
||||
victim = c.getChannelServer().getPlayerStorage().getCharacterByName(slea.readMapleAsciiString());
|
||||
if (victim != null) {
|
||||
if (type == 0) {
|
||||
c.getPlayer().changeMap(victim.getMap(), victim.getMap().getPortal(0));
|
||||
if(victim != null && victim != c.getPlayer()) {
|
||||
if(victim.getFamily() == c.getPlayer().getFamily()) {
|
||||
MapleMap targetMap = victim.getMap();
|
||||
MapleMap ownMap = c.getPlayer().getMap();
|
||||
if(targetMap != null) {
|
||||
if(type == MapleFamilyEntitlement.FAMILY_REUINION) {
|
||||
if(!FieldLimit.CANNOTMIGRATE.check(ownMap.getFieldLimit()) && !FieldLimit.CANNOTVIPROCK.check(targetMap.getFieldLimit())
|
||||
&& (targetMap.getForcedReturnId() == 999999999 || targetMap.getId() < 100000000) && targetMap.getEventInstance() == null) {
|
||||
|
||||
c.getPlayer().changeMap(victim.getMap(), victim.getMap().getPortal(0));
|
||||
useEntitlement(entry, type);
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(75, 0)); // wrong message, but close enough. (client should check this first anyway)
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if(!FieldLimit.CANNOTMIGRATE.check(targetMap.getFieldLimit()) && !FieldLimit.CANNOTVIPROCK.check(ownMap.getFieldLimit())
|
||||
&& (ownMap.getForcedReturnId() == 999999999 || ownMap.getId() < 100000000) && ownMap.getEventInstance() == null) {
|
||||
|
||||
if(MapleInviteCoordinator.hasInvite(InviteType.FAMILY_SUMMON, victim.getId())) {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(74, 0));
|
||||
return;
|
||||
}
|
||||
MapleInviteCoordinator.createInvite(InviteType.FAMILY_SUMMON, c.getPlayer(), victim, victim.getId(), c.getPlayer().getMap());
|
||||
victim.announce(MaplePacketCreator.sendFamilySummonRequest(c.getPlayer().getFamily().getName(), c.getPlayer().getName()));
|
||||
useEntitlement(entry, type);
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(75, 0));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
victim.changeMap(c.getPlayer().getMap(), c.getPlayer().getMap().getPortal(0));
|
||||
c.announce(MaplePacketCreator.sendFamilyMessage(67, 0));
|
||||
}
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else if(type == MapleFamilyEntitlement.FAMILY_BONDING) {
|
||||
//not implemented
|
||||
} else {
|
||||
int erate = type == 3 ? 150 : (type == 4 || type == 6 || type == 8 || type == 10 ? 200 : 100);
|
||||
int drate = type == 2 ? 150 : (type == 4 || type == 5 || type == 7 || type == 9 ? 200 : 100);
|
||||
if (type > 8) {
|
||||
} else {
|
||||
c.announce(useRep(drate == 100 ? 2 : (erate == 100 ? 3 : 4), type, erate, drate, ((type > 5 || type == 4) ? 2 : 1) * 15 * 60 * 1000));
|
||||
}
|
||||
boolean party = false;
|
||||
boolean isExp = false;
|
||||
float rate = 1.5f;
|
||||
int duration = 15;
|
||||
do {
|
||||
switch(type) {
|
||||
case PARTY_EXP_2_30MIN:
|
||||
party = true;
|
||||
isExp = true;
|
||||
type = MapleFamilyEntitlement.SELF_EXP_2_30MIN;
|
||||
continue;
|
||||
case PARTY_DROP_2_30MIN:
|
||||
party = true;
|
||||
type = MapleFamilyEntitlement.SELF_DROP_2_30MIN;
|
||||
continue;
|
||||
case SELF_DROP_2_30MIN:
|
||||
duration = 30;
|
||||
case SELF_DROP_2:
|
||||
rate = 2.0f;
|
||||
case SELF_DROP_1_5:
|
||||
break;
|
||||
case SELF_EXP_2_30MIN:
|
||||
duration = 30;
|
||||
case SELF_EXP_2:
|
||||
rate = 2.0f;
|
||||
case SELF_EXP_1_5:
|
||||
isExp = true;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
break;
|
||||
} while(true);
|
||||
//not implemented
|
||||
}
|
||||
c.getPlayer().getFamily().getMember(c.getPlayer().getId()).gainReputation(repCost[type]);
|
||||
}
|
||||
|
||||
/**
|
||||
* [65 00][02][08 00 00 00][C8 00 00 00][00 00 00 00][00][40 77 1B 00]
|
||||
*/
|
||||
private static byte[] useRep(int mode, int type, int erate, int drate, int time) {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(0x60);//noty
|
||||
mplew.write(mode);
|
||||
mplew.writeInt(type);
|
||||
if (mode < 4) {
|
||||
mplew.writeInt(erate);
|
||||
mplew.writeInt(drate);
|
||||
|
||||
private boolean useEntitlement(MapleFamilyEntry entry, MapleFamilyEntitlement entitlement) {
|
||||
if(entry.useEntitlement(entitlement)) {
|
||||
entry.gainReputation(-entitlement.getRepCost(), false);
|
||||
entry.getChr().announce(MaplePacketCreator.getFamilyInfo(entry));
|
||||
return true;
|
||||
}
|
||||
mplew.write(0);
|
||||
mplew.writeInt(time);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
//20 00
|
||||
//00 00 00 00
|
||||
//00 00 00 00 00 00 00 00
|
||||
//80 01
|
||||
//00 00 28 00
|
||||
//8C 93 3E 00
|
||||
//40 0D
|
||||
//03 00 14 00
|
||||
//8C 93 3E 00
|
||||
//40 0D 03 00 00 00 00 00 02
|
||||
private static byte[] giveBuff() {
|
||||
MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.GIVE_BUFF.getValue());
|
||||
mplew.writeInt(0);
|
||||
mplew.writeLong(0);
|
||||
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ import net.AbstractMaplePacketHandler;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.MapleInviteCoordinator.MapleInviteResult;
|
||||
import net.server.world.MapleMessenger;
|
||||
import net.server.world.MapleMessengerCharacter;
|
||||
import net.server.world.World;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
public final class MessengerHandler extends AbstractMaplePacketHandler {
|
||||
@@ -58,8 +58,8 @@ public final class MessengerHandler extends AbstractMaplePacketHandler {
|
||||
} else {
|
||||
messenger = world.getMessenger(messengerid);
|
||||
if (messenger != null) {
|
||||
Pair<InviteResult, MapleCharacter> inviteRes = MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), messengerid, true);
|
||||
InviteResult res = inviteRes.getLeft();
|
||||
MapleInviteResult inviteRes = MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), messengerid, true);
|
||||
InviteResult res = inviteRes.result;
|
||||
if (res == InviteResult.ACCEPTED) {
|
||||
int position = messenger.getLowestPosition();
|
||||
MapleMessengerCharacter messengerplayer = new MapleMessengerCharacter(player, position);
|
||||
|
||||
41
src/net/server/channel/handlers/OpenFamilyHandler.java
Normal file
41
src/net/server/channel/handlers/OpenFamilyHandler.java
Normal file
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ubaware
|
||||
*/
|
||||
public final class OpenFamilyHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if(!ServerConstants.USE_FAMILY_SYSTEM) return;
|
||||
MapleCharacter chr = c.getPlayer();
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(chr.getFamilyEntry()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
This file is part of the HeavenMS MapleStory Server
|
||||
Copyleft (L) 2016 - 2018 RonanLana
|
||||
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU Affero General Public License as
|
||||
published by the Free Software Foundation version 3 as published by
|
||||
the Free Software Foundation. You may not use, modify or distribute
|
||||
this program under any other version of the GNU Affero General Public
|
||||
License.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Affero General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Affero General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package net.server.channel.handlers;
|
||||
|
||||
import constants.ServerConstants;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.AbstractMaplePacketHandler;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Ubaware
|
||||
*/
|
||||
public final class OpenFamilyPedigreeHandler extends AbstractMaplePacketHandler {
|
||||
@Override
|
||||
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
|
||||
if(!ServerConstants.USE_FAMILY_SYSTEM) return;
|
||||
MapleCharacter target = c.getChannelServer().getPlayerStorage().getCharacterByName(slea.readMapleAsciiString());
|
||||
if(target != null && target.getFamily() != null) {
|
||||
c.announce(MaplePacketCreator.showPedigree(target.getFamilyEntry()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ import constants.ServerConstants;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import tools.Pair;
|
||||
import net.server.coordinator.MapleInviteCoordinator.MapleInviteResult;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@@ -64,8 +64,8 @@ public final class PartyOperationHandler extends AbstractMaplePacketHandler {
|
||||
case 3: { // join
|
||||
int partyid = slea.readInt();
|
||||
|
||||
Pair<InviteResult, MapleCharacter> inviteRes = MapleInviteCoordinator.answerInvite(InviteType.PARTY, player.getId(), partyid, true);
|
||||
InviteResult res = inviteRes.getLeft();
|
||||
MapleInviteResult inviteRes = MapleInviteCoordinator.answerInvite(InviteType.PARTY, player.getId(), partyid, true);
|
||||
InviteResult res = inviteRes.result;
|
||||
if (res == InviteResult.ACCEPTED) {
|
||||
MapleParty.joinParty(player, partyid, false);
|
||||
} else {
|
||||
|
||||
@@ -40,6 +40,7 @@ import net.server.world.MaplePartyCharacter;
|
||||
import net.server.world.PartyOperation;
|
||||
import net.server.world.World;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.FilePrinter;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.data.input.SeekableLittleEndianAccessor;
|
||||
@@ -50,6 +51,7 @@ import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.MapleDisease;
|
||||
import client.MapleFamily;
|
||||
import client.MapleFamilyEntry;
|
||||
import client.MapleKeyBinding;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.Equip;
|
||||
@@ -260,12 +262,22 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
||||
c.announce(MaplePacketCreator.loadFamily(player));
|
||||
if (player.getFamilyId() > 0) {
|
||||
MapleFamily f = wserv.getFamily(player.getFamilyId());
|
||||
if (f == null) {
|
||||
f = new MapleFamily(player.getId());
|
||||
wserv.addFamily(player.getFamilyId(), f);
|
||||
if(f != null) {
|
||||
MapleFamilyEntry familyEntry = f.getEntryByID(player.getId());
|
||||
if(familyEntry != null) {
|
||||
familyEntry.setCharacter(player);
|
||||
player.setFamilyEntry(familyEntry);
|
||||
} else {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Player " + player.getName() + "'s family doesn't have an entry for them. (" + f.getID() + ")");
|
||||
}
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(familyEntry));
|
||||
familyEntry.announceToSenior(MaplePacketCreator.sendFamilyLoginNotice(player.getName(), true), true);
|
||||
} else {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, "Player " + player.getName() + " has an invalid family ID. (" + player.getFamilyId() + ")");
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(null));
|
||||
}
|
||||
player.setFamily(f);
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(f.getMember(player.getId())));
|
||||
} else {
|
||||
c.announce(MaplePacketCreator.getFamilyInfo(null));
|
||||
}
|
||||
if (player.getGuildId() > 0) {
|
||||
MapleGuild playerGuild = server.getGuild(player.getGuildId(), player.getWorld(), player);
|
||||
@@ -410,6 +422,10 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
|
||||
if (ServerConstants.USE_NPCS_SCRIPTABLE) {
|
||||
c.announce(MaplePacketCreator.setNPCScriptable(ScriptableNPCConstants.SCRIPTABLE_NPCS));
|
||||
}
|
||||
|
||||
if(newcomer) player.setLoginTime(System.currentTimeMillis());
|
||||
} catch(Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
c.releaseClient();
|
||||
}
|
||||
|
||||
@@ -427,17 +427,9 @@ public final class UseCashItemHandler extends AbstractMaplePacketHandler {
|
||||
slea.readByte();
|
||||
slea.readInt();
|
||||
if(itemId == 5400000) { //name change
|
||||
if(player.cancelPendingNameChange()) {
|
||||
player.dropMessage(1, "Successfully canceled pending name change.");
|
||||
} else {
|
||||
player.dropMessage(1, "You do not have a pending name change.");
|
||||
}
|
||||
c.announce(MaplePacketCreator.showNameChangeCancel(player.cancelPendingNameChange()));
|
||||
} else if(itemId == 5401000) { //world transfer
|
||||
if(player.cancelPendingWorldTranfer()) {
|
||||
player.dropMessage(1, "Successfully canceled pending world transfer.");
|
||||
} else {
|
||||
player.dropMessage(1, "You do not have a pending world transfer.");
|
||||
}
|
||||
c.announce(MaplePacketCreator.showWorldTransferCancel(player.cancelPendingWorldTranfer()));
|
||||
}
|
||||
remove(c, position, itemId);
|
||||
c.announce(MaplePacketCreator.enableActions());
|
||||
|
||||
@@ -42,7 +42,8 @@ public class MapleInviteCoordinator {
|
||||
|
||||
public enum InviteType {
|
||||
//BUDDY, (not needed)
|
||||
//FAMILY, (not implemented)
|
||||
FAMILY,
|
||||
FAMILY_SUMMON,
|
||||
MESSENGER,
|
||||
TRADE,
|
||||
PARTY,
|
||||
@@ -52,11 +53,13 @@ public class MapleInviteCoordinator {
|
||||
final ConcurrentHashMap<Integer, Object> invites;
|
||||
final ConcurrentHashMap<Integer, MapleCharacter> inviteFrom;
|
||||
final ConcurrentHashMap<Integer, Integer> inviteTimeouts;
|
||||
final ConcurrentHashMap<Integer, Object[]> inviteParams;
|
||||
|
||||
private InviteType() {
|
||||
invites = new ConcurrentHashMap<>();
|
||||
inviteTimeouts = new ConcurrentHashMap<>();
|
||||
inviteFrom = new ConcurrentHashMap<>();
|
||||
inviteParams = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
private Map<Integer, Object> getRequestsTable() {
|
||||
@@ -67,15 +70,15 @@ public class MapleInviteCoordinator {
|
||||
return inviteTimeouts;
|
||||
}
|
||||
|
||||
private MapleCharacter removeRequest(Integer target) {
|
||||
private Pair<MapleCharacter, Object[]> removeRequest(Integer target) {
|
||||
invites.remove(target);
|
||||
MapleCharacter from = inviteFrom.remove(target);
|
||||
inviteTimeouts.remove(target);
|
||||
|
||||
return from;
|
||||
return new Pair<>(from, inviteParams.remove(target));
|
||||
}
|
||||
|
||||
private boolean addRequest(MapleCharacter from, Object referenceFrom, int targetCid) {
|
||||
private boolean addRequest(MapleCharacter from, Object referenceFrom, int targetCid, Object[] params) {
|
||||
Object v = invites.putIfAbsent(targetCid, referenceFrom);
|
||||
if (v != null) { // there was already an entry
|
||||
return false;
|
||||
@@ -83,7 +86,7 @@ public class MapleInviteCoordinator {
|
||||
|
||||
inviteFrom.put(targetCid, from);
|
||||
inviteTimeouts.put(targetCid, 0);
|
||||
|
||||
inviteParams.put(targetCid, params);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -93,29 +96,31 @@ public class MapleInviteCoordinator {
|
||||
}
|
||||
|
||||
// note: referenceFrom is a specific value that represents the "common association" created between the sender/recver parties
|
||||
public static boolean createInvite(InviteType type, MapleCharacter from, Object referenceFrom, int targetCid) {
|
||||
return type.addRequest(from, referenceFrom, targetCid);
|
||||
public static boolean createInvite(InviteType type, MapleCharacter from, Object referenceFrom, int targetCid, Object... params) {
|
||||
return type.addRequest(from, referenceFrom, targetCid, params);
|
||||
}
|
||||
|
||||
public static boolean hasInvite(InviteType type, int targetCid) {
|
||||
return type.hasRequest(targetCid);
|
||||
}
|
||||
|
||||
public static Pair<InviteResult, MapleCharacter> answerInvite(InviteType type, int targetCid, Object referenceFrom, boolean answer) {
|
||||
public static MapleInviteResult answerInvite(InviteType type, int targetCid, Object referenceFrom, boolean answer) {
|
||||
Map<Integer, Object> table = type.getRequestsTable();
|
||||
|
||||
MapleCharacter from = null;
|
||||
InviteResult result = InviteResult.NOT_FOUND;
|
||||
Pair<MapleCharacter, Object[]> inviteInfo = null;
|
||||
|
||||
Object reference = table.get(targetCid);
|
||||
if (referenceFrom.equals(reference)) {
|
||||
from = type.removeRequest(targetCid);
|
||||
inviteInfo = type.removeRequest(targetCid);
|
||||
from = inviteInfo.getLeft();
|
||||
if (from != null && !from.isLoggedinWorld()) from = null;
|
||||
|
||||
result = answer ? InviteResult.ACCEPTED : InviteResult.DENIED;
|
||||
}
|
||||
|
||||
return new Pair<>(result, from);
|
||||
return new MapleInviteResult(result, from, inviteInfo != null ? inviteInfo.getRight() : new Object[0]);
|
||||
}
|
||||
|
||||
public static void removeInvite(InviteType type, int targetCid) {
|
||||
@@ -146,4 +151,17 @@ public class MapleInviteCoordinator {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class MapleInviteResult {
|
||||
|
||||
public final InviteResult result;
|
||||
public final MapleCharacter from;
|
||||
public final Object[] params;
|
||||
|
||||
private MapleInviteResult(InviteResult result, MapleCharacter from, Object[] params) {
|
||||
this.result = result;
|
||||
this.from = from;
|
||||
this.params = params;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,13 +32,12 @@ import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import net.server.Server;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.MapleInviteCoordinator.MapleInviteResult;
|
||||
import net.server.world.MapleParty;
|
||||
import net.server.world.MaplePartyCharacter;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -494,11 +493,11 @@ public class MapleAlliance {
|
||||
}
|
||||
|
||||
public static boolean answerInvitation(int targetId, String targetGuildName, int allianceId, boolean answer) {
|
||||
Pair<InviteResult, MapleCharacter> res = MapleInviteCoordinator.answerInvite(InviteType.ALLIANCE, targetId, allianceId, answer);
|
||||
MapleInviteResult res = MapleInviteCoordinator.answerInvite(InviteType.ALLIANCE, targetId, allianceId, answer);
|
||||
|
||||
String msg;
|
||||
MapleCharacter sender = res.getRight();
|
||||
switch (res.getLeft()) {
|
||||
MapleCharacter sender = res.from;
|
||||
switch (res.result) {
|
||||
case ACCEPTED:
|
||||
return true;
|
||||
|
||||
|
||||
@@ -45,11 +45,10 @@ import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.MaplePacketCreator;
|
||||
import tools.Pair;
|
||||
import net.server.audit.locks.MonitoredLockType;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.MapleInviteResult;
|
||||
import net.server.coordinator.MapleMatchCheckerCoordinator;
|
||||
|
||||
public class MapleGuild {
|
||||
@@ -727,11 +726,11 @@ public class MapleGuild {
|
||||
}
|
||||
|
||||
public static boolean answerInvitation(int targetId, String targetName, int guildId, boolean answer) {
|
||||
Pair<InviteResult, MapleCharacter> res = MapleInviteCoordinator.answerInvite(InviteType.GUILD, targetId, guildId, answer);
|
||||
MapleInviteResult res = MapleInviteCoordinator.answerInvite(InviteType.GUILD, targetId, guildId, answer);
|
||||
|
||||
MapleGuildResponse mgr;
|
||||
MapleCharacter sender = res.getRight();
|
||||
switch (res.getLeft()) {
|
||||
MapleCharacter sender = res.from;
|
||||
switch (res.result) {
|
||||
case ACCEPTED:
|
||||
return true;
|
||||
|
||||
|
||||
56
src/net/server/worker/FamilyDailyResetWorker.java
Normal file
56
src/net/server/worker/FamilyDailyResetWorker.java
Normal file
@@ -0,0 +1,56 @@
|
||||
package net.server.worker;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.Calendar;
|
||||
|
||||
import client.MapleFamily;
|
||||
import net.server.world.World;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.FilePrinter;
|
||||
|
||||
public class FamilyDailyResetWorker implements Runnable {
|
||||
|
||||
private final World world;
|
||||
|
||||
public FamilyDailyResetWorker(World world) {
|
||||
this.world = world;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
resetEntitlementUsage(world);
|
||||
for(MapleFamily family : world.getFamilies()) {
|
||||
family.resetDailyReps();
|
||||
}
|
||||
}
|
||||
|
||||
public static void resetEntitlementUsage(World world) {
|
||||
Calendar resetTime = Calendar.getInstance();
|
||||
resetTime.add(Calendar.MINUTE, 1); // to make sure that we're in the "next day", since this is called at midnight
|
||||
resetTime.set(Calendar.HOUR, 0);
|
||||
resetTime.set(Calendar.MINUTE, 0);
|
||||
resetTime.set(Calendar.SECOND, 0);
|
||||
resetTime.set(Calendar.MILLISECOND, 0);
|
||||
try(Connection con = DatabaseConnection.getConnection()) {
|
||||
try(PreparedStatement ps = con.prepareStatement("UPDATE family_character SET todaysrep = 0, reptosenior = 0 WHERE lastresettime <= ?")) {
|
||||
ps.setLong(1, resetTime.getTimeInMillis());
|
||||
ps.executeUpdate();
|
||||
} catch(SQLException e) {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not reset daily rep for families. On " + Calendar.getInstance().getTime());
|
||||
e.printStackTrace();
|
||||
}
|
||||
try(PreparedStatement ps = con.prepareStatement("DELETE FROM family_entitlement WHERE timestamp <= ?")) {
|
||||
ps.setLong(1, resetTime.getTimeInMillis());
|
||||
ps.executeUpdate();
|
||||
} catch(SQLException e) {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not do daily reset for family entitlements. On " + Calendar.getInstance().getTime());
|
||||
e.printStackTrace();
|
||||
}
|
||||
} catch(SQLException e) {
|
||||
FilePrinter.printError(FilePrinter.FAMILY_ERROR, e, "Could not get connection to DB.");
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ import server.maps.MaplePlayerShop;
|
||||
import server.maps.MaplePlayerShopItem;
|
||||
import server.maps.AbstractMapleMapObject;
|
||||
import net.server.worker.CharacterAutosaverWorker;
|
||||
import net.server.worker.FamilyDailyResetWorker;
|
||||
import net.server.worker.FishingWorker;
|
||||
import net.server.worker.HiredMerchantWorker;
|
||||
import net.server.worker.MapOwnershipWorker;
|
||||
@@ -211,6 +212,11 @@ public class World {
|
||||
fishingSchedule = tman.register(new FishingWorker(this), 10 * 1000, 10 * 1000);
|
||||
partySearchSchedule = tman.register(new PartySearchWorker(this), 10 * 1000, 10 * 1000);
|
||||
|
||||
if(ServerConstants.USE_FAMILY_SYSTEM) {
|
||||
long timeLeft = Server.getTimeLeftForNextDay();
|
||||
FamilyDailyResetWorker.resetEntitlementUsage(this);
|
||||
tman.register(new FamilyDailyResetWorker(this), 24 * 60 * 60 * 1000, timeLeft);
|
||||
}
|
||||
}
|
||||
|
||||
public int getChannelsSize() {
|
||||
@@ -540,6 +546,12 @@ public class World {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeFamily(int id) {
|
||||
synchronized (families) {
|
||||
families.remove(id);
|
||||
}
|
||||
}
|
||||
|
||||
public MapleFamily getFamily(int id) {
|
||||
synchronized (families) {
|
||||
@@ -549,6 +561,12 @@ public class World {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public Collection<MapleFamily> getFamilies() {
|
||||
synchronized(families) {
|
||||
return Collections.unmodifiableCollection((Collection<MapleFamily>) families.values());
|
||||
}
|
||||
}
|
||||
|
||||
public MapleGuild getGuild(MapleGuildCharacter mgc) {
|
||||
if(mgc == null) return null;
|
||||
@@ -1109,7 +1127,7 @@ public class World {
|
||||
if (isConnected(sender)) {
|
||||
MapleCharacter senderChr = getPlayerStorage().getCharacterByName(sender);
|
||||
if (senderChr != null && senderChr.getMessenger() != null) {
|
||||
if (MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), senderChr.getMessenger().getId(), false).getLeft() == InviteResult.DENIED) {
|
||||
if (MapleInviteCoordinator.answerInvite(InviteType.MESSENGER, player.getId(), senderChr.getMessenger().getId(), false).result == InviteResult.DENIED) {
|
||||
senderChr.getClient().announce(MaplePacketCreator.messengerNote(player.getName(), 5, 0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import constants.ServerConstants;
|
||||
import net.server.coordinator.MapleInviteCoordinator;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteResult;
|
||||
import net.server.coordinator.MapleInviteCoordinator.InviteType;
|
||||
import net.server.coordinator.MapleInviteCoordinator.MapleInviteResult;
|
||||
import tools.Pair;
|
||||
|
||||
/**
|
||||
@@ -476,9 +477,9 @@ public class MapleTrade {
|
||||
}
|
||||
|
||||
public static void visitTrade(MapleCharacter c1, MapleCharacter c2) {
|
||||
Pair<InviteResult, MapleCharacter> inviteRes = MapleInviteCoordinator.answerInvite(InviteType.TRADE, c1.getId(), c2.getId(), true);
|
||||
MapleInviteResult inviteRes = MapleInviteCoordinator.answerInvite(InviteType.TRADE, c1.getId(), c2.getId(), true);
|
||||
|
||||
InviteResult res = inviteRes.getLeft();
|
||||
InviteResult res = inviteRes.result;
|
||||
if (res == InviteResult.ACCEPTED) {
|
||||
if (c1.getTrade() != null && c1.getTrade().getPartner() == c2.getTrade() && c2.getTrade() != null && c2.getTrade().getPartner() == c1.getTrade()) {
|
||||
c2.getClient().announce(MaplePacketCreator.getTradePartnerAdd(c1));
|
||||
@@ -499,7 +500,7 @@ public class MapleTrade {
|
||||
if (trade != null) {
|
||||
if (trade.getPartner() != null) {
|
||||
MapleCharacter other = trade.getPartner().getChr();
|
||||
if (MapleInviteCoordinator.answerInvite(InviteType.TRADE, c.getId(), other.getId(), false).getLeft() == InviteResult.DENIED) {
|
||||
if (MapleInviteCoordinator.answerInvite(InviteType.TRADE, c.getId(), other.getId(), false).result == InviteResult.DENIED) {
|
||||
other.message(c.getName() + " has declined your trade request.");
|
||||
}
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ package server.life;
|
||||
import client.MapleBuffStat;
|
||||
import client.MapleCharacter;
|
||||
import client.MapleClient;
|
||||
import client.MapleFamilyEntry;
|
||||
import client.MapleJob;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
@@ -522,6 +523,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
float bonusExp = partyBonusMod * playerExp;
|
||||
|
||||
this.giveExpToCharacter(chr, playerExp, bonusExp, whiteExpGain, hasPartySharers);
|
||||
giveFamilyRep(chr.getFamilyEntry());
|
||||
}
|
||||
|
||||
private void distributePartyExperience(Map<MapleCharacter, Long> partyParticipation, float expPerDmg, Set<MapleCharacter> underleveled, Map<Integer, Float> personalRatio, double sdevRatio) {
|
||||
@@ -548,7 +550,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
int totalPartyLevel = 0;
|
||||
|
||||
// thanks G h o s t, Alfred, Vcoc, BHB for poiting out a bug in detecting party members after membership transactions in a party took place
|
||||
if (!ServerConstants.USE_ENFORCE_MOB_LEVEL_RANGE) {
|
||||
if (ServerConstants.USE_ENFORCE_MOB_LEVEL_RANGE) {
|
||||
for (MapleCharacter member : partyParticipation.keySet().iterator().next().getPartyMembersOnSameMap()) {
|
||||
if (!leechInterval.inInterval(member.getLevel())) {
|
||||
underleveled.add(member);
|
||||
@@ -574,6 +576,7 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
|
||||
for (MapleCharacter mc : expMembers) {
|
||||
distributePlayerExperience(mc, participationExp, partyBonusMod, totalPartyLevel, mc == participationMvp, isWhiteExpGain(mc, personalRatio, sdevRatio), hasPartySharers);
|
||||
giveFamilyRep(mc.getFamilyEntry());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -949,6 +952,14 @@ public class MapleMonster extends AbstractLoadedMapleLife {
|
||||
listener.monsterHealed(trueHeal);
|
||||
}
|
||||
}
|
||||
|
||||
private void giveFamilyRep(MapleFamilyEntry entry) {
|
||||
if(entry != null) {
|
||||
int repGain = isBoss() ? ServerConstants.FAMILY_REP_PER_BOSS_KILL : ServerConstants.FAMILY_REP_PER_KILL;
|
||||
if(getMaxHp() <= 1) repGain = 0; //don't count trash mobs
|
||||
entry.giveReputationToSenior(repGain, true);
|
||||
}
|
||||
}
|
||||
|
||||
public int getHighestDamagerId() {
|
||||
int curId = 0;
|
||||
|
||||
@@ -61,6 +61,7 @@ public class FilePrinter {
|
||||
SAVING_CHARACTER = "players/SaveChar.txt",
|
||||
CHANGE_CHARACTER_NAME = "players/NameChange.txt",
|
||||
WORLD_TRANSFER = "players/WorldTransfer.txt",
|
||||
FAMILY_ERROR = "players/FamilyErrors.txt",
|
||||
USED_COMMANDS = "commands/UsedCommands.txt",
|
||||
DEADLOCK_ERROR = "deadlocks/Deadlocks.txt",
|
||||
DEADLOCK_STACK = "deadlocks/Path.txt",
|
||||
|
||||
@@ -41,6 +41,7 @@ import client.MapleCharacter;
|
||||
import client.MapleCharacter.SkillEntry;
|
||||
import client.MapleClient;
|
||||
import client.MapleDisease;
|
||||
import client.MapleFamilyEntitlement;
|
||||
import client.MapleFamilyEntry;
|
||||
import client.MapleKeyBinding;
|
||||
import client.MapleMount;
|
||||
@@ -1030,7 +1031,7 @@ public class MaplePacketCreator {
|
||||
for (Pair<MapleStat, Integer> statupdate : mystats) {
|
||||
if (statupdate.getLeft().getValue() >= 1) {
|
||||
if (statupdate.getLeft().getValue() == 0x1) {
|
||||
mplew.writeShort(statupdate.getRight().shortValue());
|
||||
mplew.write(statupdate.getRight().byteValue());
|
||||
} else if (statupdate.getLeft().getValue() <= 0x4) {
|
||||
mplew.writeInt(statupdate.getRight());
|
||||
} else if (statupdate.getLeft().getValue() < 0x20) {
|
||||
@@ -1043,6 +1044,8 @@ public class MaplePacketCreator {
|
||||
}
|
||||
} else if (statupdate.getLeft().getValue() < 0xFFFF) {
|
||||
mplew.writeShort(statupdate.getRight().shortValue());
|
||||
} else if (statupdate.getLeft().getValue() == 0x20000) {
|
||||
mplew.writeShort(statupdate.getRight().shortValue());
|
||||
} else {
|
||||
mplew.writeInt(statupdate.getRight().intValue());
|
||||
}
|
||||
@@ -2045,7 +2048,8 @@ public class MaplePacketCreator {
|
||||
addRingLook(mplew, chr, false); // friendship
|
||||
addMarriageRingLook(target, mplew, chr);
|
||||
encodeNewYearCardInfo(mplew, chr); // new year seems to crash sometimes...
|
||||
mplew.skip(2);
|
||||
mplew.write(0);
|
||||
mplew.write(0);
|
||||
mplew.write(chr.getTeam());//only needed in specific fields
|
||||
return mplew.getPacket();
|
||||
}
|
||||
@@ -6150,6 +6154,24 @@ public class MaplePacketCreator {
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showNameChangeCancel(boolean success) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.CANCEL_NAME_CHANGE_RESULT.getValue());
|
||||
mplew.writeBool(success);
|
||||
if(!success) mplew.write(0);
|
||||
//mplew.writeMapleAsciiString("Custom message."); //only if ^ != 0
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showWorldTransferCancel(boolean success) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.CANCEL_TRANSFER_WORLD_RESULT.getValue());
|
||||
mplew.writeBool(success);
|
||||
if(!success) mplew.write(0);
|
||||
//mplew.writeMapleAsciiString("Custom message."); //only if ^ != 0
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showMTSCash(MapleCharacter p) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.MTS_OPERATION2.getValue());
|
||||
@@ -6376,18 +6398,16 @@ public class MaplePacketCreator {
|
||||
}
|
||||
|
||||
public static byte[] loadFamily(MapleCharacter player) {
|
||||
String[] title = {"Family Reunion", "Summon Family", "My Drop Rate 1.5x (15 min)", "My EXP 1.5x (15 min)", "Family Bonding (30 min)", "My Drop Rate 2x (15 min)", "My EXP 2x (15 min)", "My Drop Rate 2x (30 min)", "My EXP 2x (30 min)", "My Party Drop Rate 2x (30 min)", "My Party EXP 2x (30 min)"};
|
||||
String[] description = {"[Target] Me\n[Effect] Teleport directly to the Family member of your choice.", "[Target] 1 Family member\n[Effect] Summon a Family member of choice to the map you're in.", "[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.", "[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.", "[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.", "[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.", "[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.", "[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.", "[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.", "[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.", "[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."};
|
||||
int[] repCost = {3, 5, 7, 8, 10, 12, 15, 20, 25, 40, 50};
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_PRIVILEGE_LIST.getValue());
|
||||
mplew.writeInt(11);
|
||||
for (int i = 0; i < 11; i++) {
|
||||
mplew.write(i > 4 ? (i % 2) + 1 : i);
|
||||
mplew.writeInt(repCost[i] * 100);
|
||||
mplew.writeInt(1);
|
||||
mplew.writeMapleAsciiString(title[i]);
|
||||
mplew.writeMapleAsciiString(description[i]);
|
||||
mplew.writeInt(MapleFamilyEntitlement.values().length);
|
||||
for (int i = 0; i < MapleFamilyEntitlement.values().length; i++) {
|
||||
MapleFamilyEntitlement entitlement = MapleFamilyEntitlement.values()[i];
|
||||
mplew.write(i <= 1 ? 1 : 2); //type
|
||||
mplew.writeInt(entitlement.getRepCost());
|
||||
mplew.writeInt(entitlement.getUsageLimit());
|
||||
mplew.writeMapleAsciiString(entitlement.getName());
|
||||
mplew.writeMapleAsciiString(entitlement.getDescription());
|
||||
}
|
||||
return mplew.getPacket();
|
||||
}
|
||||
@@ -6396,6 +6416,9 @@ public class MaplePacketCreator {
|
||||
* Family Result Message
|
||||
*
|
||||
* Possible values for <code>type</code>:<br>
|
||||
* 64: You cannot add this character as a junior.
|
||||
* 65: The name could not be found or is not online.
|
||||
* 66: You belong to the same family.
|
||||
* 67: You do not belong to the same family.<br>
|
||||
* 69: The character you wish to add as\r\na Junior must be in the same
|
||||
* map.<br>
|
||||
@@ -6433,27 +6456,121 @@ public class MaplePacketCreator {
|
||||
}
|
||||
|
||||
public static byte[] getFamilyInfo(MapleFamilyEntry f) {
|
||||
if(f == null) return getEmptyFamilyInfo();
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_INFO_RESULT.getValue());
|
||||
mplew.writeInt(f.getReputation()); // cur rep left
|
||||
mplew.writeInt(f.getTotalReputation()); // tot rep left
|
||||
mplew.writeInt(f.getTodaysRep()); // todays rep
|
||||
mplew.writeShort(f.getJuniors()); // juniors added
|
||||
mplew.writeShort(f.getTotalJuniors()); // juniors allowed
|
||||
mplew.writeShort(f.getJuniorCount()); // juniors added
|
||||
mplew.writeShort(2); // juniors allowed
|
||||
mplew.writeShort(0); //Unknown
|
||||
mplew.writeInt(f.getId()); // id?
|
||||
mplew.writeMapleAsciiString(f.getFamilyName());
|
||||
mplew.writeInt(f.getFamily().getLeader().getChrId()); // Leader ID (Allows setting message)
|
||||
mplew.writeMapleAsciiString(f.getFamily().getName());
|
||||
mplew.writeMapleAsciiString(f.getFamily().getMessage()); //family message
|
||||
mplew.writeInt(MapleFamilyEntitlement.values().length); //Entitlement info count
|
||||
for(MapleFamilyEntitlement entitlement : MapleFamilyEntitlement.values()) {
|
||||
mplew.writeInt(entitlement.ordinal()); //ID
|
||||
mplew.writeInt(f.isEntitlementUsed(entitlement) ? 1 : 0); //Used count
|
||||
}
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
private static byte[] getEmptyFamilyInfo() {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_INFO_RESULT.getValue());
|
||||
mplew.writeInt(0); // cur rep left
|
||||
mplew.writeInt(0); // tot rep left
|
||||
mplew.writeInt(0); // todays rep
|
||||
mplew.writeShort(0); // juniors added
|
||||
mplew.writeShort(2); // juniors allowed
|
||||
mplew.writeShort(0); //Unknown
|
||||
mplew.writeInt(0); // Leader ID (Allows setting message)
|
||||
mplew.writeMapleAsciiString("");
|
||||
mplew.writeMapleAsciiString(""); //family message
|
||||
mplew.writeInt(0);
|
||||
mplew.writeShort(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showPedigree(int chrid, Map<Integer, MapleFamilyEntry> members) {
|
||||
public static byte[] showPedigree(MapleFamilyEntry entry) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_CHART_RESULT.getValue());
|
||||
//Hmmm xD
|
||||
mplew.writeInt(entry.getChrId()); //ID of viewed player's pedigree, can't be leader?
|
||||
List<MapleFamilyEntry> superJuniors = new ArrayList<MapleFamilyEntry>(4);
|
||||
boolean hasOtherJunior = false;
|
||||
int entryCount = 2; //2 guaranteed, leader and self
|
||||
entryCount += Math.min(2, entry.getTotalSeniors());
|
||||
//needed since MaplePacketLittleEndianWriter doesn't have any seek functionality
|
||||
if(entry.getSenior() != null) {
|
||||
if(entry.getSenior().getJuniorCount() == 2) {
|
||||
entryCount++;
|
||||
hasOtherJunior = true;
|
||||
}
|
||||
}
|
||||
for(MapleFamilyEntry junior : entry.getJuniors()) {
|
||||
if(junior == null) continue;
|
||||
entryCount++;
|
||||
for(MapleFamilyEntry superJunior : junior.getJuniors()) {
|
||||
if(superJunior == null) continue;
|
||||
entryCount++;
|
||||
superJuniors.add(superJunior);
|
||||
}
|
||||
}
|
||||
//write entries
|
||||
boolean missingEntries = entryCount == 2; //pedigree requires at least 3 entries to show leader, might only have 2 if leader's juniors leave
|
||||
if(missingEntries) entryCount++;
|
||||
mplew.writeInt(entryCount); //player count
|
||||
addPedigreeEntry(mplew, entry.getFamily().getLeader());
|
||||
if(entry.getSenior() != null) {
|
||||
if(entry.getSenior().getSenior() != null) addPedigreeEntry(mplew, entry.getSenior().getSenior());
|
||||
addPedigreeEntry(mplew, entry.getSenior());
|
||||
}
|
||||
addPedigreeEntry(mplew, entry);
|
||||
if(hasOtherJunior) { //must be sent after own entry
|
||||
MapleFamilyEntry otherJunior = entry.getSenior().getOtherJunior(entry);
|
||||
if(otherJunior != null) addPedigreeEntry(mplew, otherJunior);
|
||||
}
|
||||
if(missingEntries) addPedigreeEntry(mplew, entry);
|
||||
for(MapleFamilyEntry junior : entry.getJuniors()) {
|
||||
if(junior == null) continue;
|
||||
addPedigreeEntry(mplew, junior);
|
||||
for(MapleFamilyEntry superJunior : junior.getJuniors()) {
|
||||
if(superJunior != null) addPedigreeEntry(mplew, superJunior);
|
||||
}
|
||||
}
|
||||
mplew.writeInt(2 + superJuniors.size()); //member info count
|
||||
// 0 = total seniors, -1 = total members, otherwise junior count of ID
|
||||
mplew.writeInt(-1);
|
||||
mplew.writeInt(entry.getFamily().getTotalMembers());
|
||||
mplew.writeInt(0);
|
||||
mplew.writeInt(entry.getTotalSeniors()); //client subtracts provided seniors
|
||||
for(MapleFamilyEntry superJunior : superJuniors) {
|
||||
mplew.writeInt(superJunior.getChrId());
|
||||
mplew.writeInt(superJunior.getTotalJuniors());
|
||||
}
|
||||
mplew.writeInt(0); //another loop count (entitlements used)
|
||||
//mplew.writeInt(1); //entitlement index
|
||||
//mplew.writeInt(2); //times used
|
||||
mplew.writeShort(entry.getJuniorCount() >= 2 ? 0 : 2); //0 disables Add button (only if viewing own pedigree)
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
private static void addPedigreeEntry(MaplePacketLittleEndianWriter mplew, MapleFamilyEntry entry) {
|
||||
MapleCharacter chr = entry.getChr();
|
||||
boolean isOnline = chr != null;
|
||||
mplew.writeInt(entry.getChrId()); //ID
|
||||
mplew.writeInt(entry.getSenior() != null ? entry.getSenior().getChrId() : 0); //parent ID
|
||||
mplew.writeShort(entry.getJob().getId()); //job id
|
||||
mplew.write(entry.getLevel()); //level
|
||||
mplew.writeBool(isOnline); //isOnline
|
||||
mplew.writeInt(entry.getReputation()); //current rep
|
||||
mplew.writeInt(entry.getTotalReputation()); //total rep
|
||||
mplew.writeInt(entry.getRepsToSenior()); //reps recorded to senior
|
||||
mplew.writeInt(entry.getTodaysRep());
|
||||
mplew.writeInt(isOnline ? ((chr.isAwayFromWorld() || chr.getCashShop().isOpened()) ? -1 : chr.getClient().getChannel() - 1) : 0);
|
||||
mplew.writeInt(isOnline ? (int) (chr.getLoggedInTime() / 60000) : 0); //time online in minutes
|
||||
mplew.writeMapleAsciiString(entry.getName()); //name
|
||||
}
|
||||
|
||||
public static byte[] updateAreaInfo(int area, String info) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
@@ -6935,6 +7052,46 @@ public class MaplePacketCreator {
|
||||
mplew.writeMapleAsciiString(inviter);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] sendFamilySummonRequest(String familyName, String from) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_SUMMON_REQUEST.getValue());
|
||||
mplew.writeMapleAsciiString(from);
|
||||
mplew.writeMapleAsciiString(familyName);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] sendFamilyLoginNotice(String name, boolean loggedIn) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_NOTIFY_LOGIN_OR_LOGOUT.getValue());
|
||||
mplew.writeBool(loggedIn);
|
||||
mplew.writeMapleAsciiString(name);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] sendFamilyJoinResponse(boolean accepted, String added) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_JOIN_REQUEST_RESULT.getValue());
|
||||
mplew.write(accepted ? 1 : 0);
|
||||
mplew.writeMapleAsciiString(added);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] getSeniorMessage(String name) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_JOIN_ACCEPTED.getValue());
|
||||
mplew.writeMapleAsciiString(name);
|
||||
mplew.writeInt(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] sendGainRep(int gain, String from) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_REP_GAIN.getValue());
|
||||
mplew.writeInt(gain);
|
||||
mplew.writeMapleAsciiString(from);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] showBoughtCashPackage(List<Item> cashPackage, int accountId) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
@@ -7176,30 +7333,6 @@ public class MaplePacketCreator {
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] sendFamilyJoinResponse(boolean accepted, String added) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_JOIN_REQUEST_RESULT.getValue());
|
||||
mplew.write(accepted ? 1 : 0);
|
||||
mplew.writeMapleAsciiString(added);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] getSeniorMessage(String name) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_JOIN_ACCEPTED.getValue());
|
||||
mplew.writeMapleAsciiString(name);
|
||||
mplew.writeInt(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] sendGainRep(int gain, int mode) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.FAMILY_FAMOUS_POINT_INC_RESULT.getValue());
|
||||
mplew.writeInt(gain);
|
||||
mplew.writeShort(0);
|
||||
return mplew.getPacket();
|
||||
}
|
||||
|
||||
public static byte[] removeItemFromDuey(boolean remove, int Package) {
|
||||
final MaplePacketLittleEndianWriter mplew = new MaplePacketLittleEndianWriter();
|
||||
mplew.writeShort(SendOpcode.PARCEL.getValue());
|
||||
|
||||
Reference in New Issue
Block a user