Added families. (#511)

* Added families.

* Added joining of families.
This commit is contained in:
Ubaware
2019-08-19 10:16:10 -07:00
committed by Ronan Lana
parent f958624f6a
commit ec5a412e37
33 changed files with 1670 additions and 276 deletions

View File

@@ -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());

View File

@@ -145,10 +145,15 @@ public enum RecvOpcode {
WEDDING_TALK_MORE(0x8B),
ALLIANCE_OPERATION(0x8F),
DENY_ALLIANCE_REQUEST(0x90),
OPEN_FAMILY_PEDIGREE(0x91),
OPEN_FAMILY(0x92),
ADD_FAMILY(0x93),
SEPARATE_FAMILY_BY_SENIOR(0x94),
SEPARATE_FAMILY_BY_JUNIOR(0x95),
ACCEPT_FAMILY(0x96),
USE_FAMILY(0x97),
CHANGE_FAMILY_MESSAGE(0x98),
FAMILY_SUMMON_RESPONSE(0x99),
BBS_OPERATION(0x9B),
ENTER_MTS(0x9C),
USE_SOLOMON_ITEM(0x9D),

View File

@@ -124,7 +124,7 @@ public enum SendOpcode {
FAMILY_JOIN_REQUEST_RESULT(0x62),
FAMILY_JOIN_ACCEPTED(0x63),
FAMILY_PRIVILEGE_LIST(0x64),
FAMILY_FAMOUS_POINT_INC_RESULT(0x65),
FAMILY_REP_GAIN(0x65),
FAMILY_NOTIFY_LOGIN_OR_LOGOUT(0x66), //? is logged in. LOLWUT
FAMILY_SET_PRIVILEGE(0x67),
FAMILY_SUMMON_REQUEST(0x68),

View File

@@ -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());

View File

@@ -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();
}
}
}

View File

@@ -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()));
}

View File

@@ -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());
}
}

View 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()));
}
}

View 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;
}
}

View File

@@ -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.");
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);

View 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()));
}
}

View File

@@ -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()));
}
}
}

View File

@@ -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 {

View File

@@ -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();
}

View File

@@ -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());

View File

@@ -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;
}
}
}

View File

@@ -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;

View File

@@ -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;

View 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();
}
}
}

View File

@@ -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));
}
}