@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user