Overall wedding implementation

Implementation of the wedding feature.
This commit is contained in:
ronancpl
2018-04-10 21:50:29 -03:00
parent 6da5edd837
commit ede8ad8551
59 changed files with 5281 additions and 1246 deletions

View File

@@ -248,6 +248,8 @@ public final class PacketProcessor {
registerHandler(RecvOpcode.MONSTER_CARNIVAL, new MonsterCarnivalHandler());
registerHandler(RecvOpcode.REMOTE_STORE, new RemoteStoreHandler());
registerHandler(RecvOpcode.WEDDING_ACTION, new WeddingHandler());
registerHandler(RecvOpcode.WEDDING_TALK, new WeddingTalkHandler());
registerHandler(RecvOpcode.WEDDING_TALK_MORE, new WeddingTalkMoreHandler());
registerHandler(RecvOpcode.WATER_OF_LIFE, new UseWaterOfLifeHandler());
registerHandler(RecvOpcode.ADMIN_CHAT, new AdminChatHandler());
registerHandler(RecvOpcode.MOVE_DRAGON, new MoveDragonHandler());

View File

@@ -114,7 +114,6 @@ public enum RecvOpcode {
QUEST_ACTION(0x6B),
//lolno
SKILL_MACRO(0x6E),
SPOUSE_CHAT(0x6F),
USE_ITEM_REWARD(0x70),
MAKER_SKILL(0x71),
USE_REMOTE(0x74),
@@ -122,6 +121,7 @@ public enum RecvOpcode {
ADMIN_CHAT(0x76),
PARTYCHAT(0x77),
WHISPER(0x78),
SPOUSE_CHAT(0x79),
MESSENGER(0x7A),
PLAYER_INTERACTION(0x7B),
PARTY_OPERATION(0x7C),
@@ -137,6 +137,8 @@ public enum RecvOpcode {
RPS_ACTION(0x88),
RING_ACTION(0x89),
WEDDING_ACTION(0x8A),
WEDDING_TALK(0x8B),
WEDDING_TALK_MORE(0x8B),
ALLIANCE_OPERATION(0x8F),
OPEN_FAMILY(0x92),
ADD_FAMILY(0x93),

View File

@@ -67,7 +67,7 @@ public enum SendOpcode {
OPEN_FULL_CLIENT_DOWNLOAD_LINK(0x28),
MEMO_RESULT(0x29),
MAP_TRANSFER_RESULT(0x2A),
ANTI_MACRO_RESULT(0x2B),
WEDDING_PHOTO(0x2B), //ANTI_MACRO_RESULT(0x2B),
CLAIM_RESULT(0x2D),
CLAIM_AVAILABLE_TIME(0x2E),
CLAIM_STATUS_CHANGED(0x2F),

View File

@@ -340,9 +340,10 @@ public class Server implements Runnable {
worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i)));
worlds.add(world);
channels.add(new HashMap<Integer, String>());
long bootTime = System.currentTimeMillis();
for (int j = 0; j < Integer.parseInt(p.getProperty("channels" + i)); j++) {
int channelid = j + 1;
Channel channel = new Channel(i, channelid);
Channel channel = new Channel(i, channelid, bootTime);
world.addChannel(channel);
channels.get(i).put(channelid, channel.getIP());
}

View File

@@ -24,6 +24,7 @@ package net.server.channel;
import java.io.File;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
@@ -41,6 +42,8 @@ import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;
import net.MapleServerHandler;
import net.mina.MapleCodecFactory;
import net.server.Server;
import net.server.world.World;
import net.server.PlayerStorage;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
@@ -65,6 +68,7 @@ import server.maps.MapleMap;
import server.maps.MapleMapFactory;
import server.maps.MapleMiniDungeon;
import tools.MaplePacketCreator;
import tools.Pair;
import client.MapleCharacter;
import constants.ServerConstants;
import server.maps.MapleMiniDungeonInfo;
@@ -92,15 +96,30 @@ public final class Channel {
private Map<Integer, Integer> dojoParty = new HashMap<>();
private Map<Integer, MapleMiniDungeon> dungeons = new HashMap<>();
private List<Integer> chapelReservationQueue = new LinkedList<>();
private List<Integer> cathedralReservationQueue = new LinkedList<>();
private ScheduledFuture<?> chapelReservationTask;
private ScheduledFuture<?> cathedralReservationTask;
private Integer ongoingChapel = null;
private Boolean ongoingChapelType = null;
private Set<Integer> ongoingChapelGuests = null;
private Integer ongoingCathedral = null;
private Boolean ongoingCathedralType = null;
private Set<Integer> ongoingCathedralGuests = null;
private long ongoingStartTime;
private ReentrantReadWriteLock merchantLock = new MonitoredReentrantReadWriteLock(MonitoredLockType.MERCHANT, true);
private ReadLock merchRlock = merchantLock.readLock();
private WriteLock merchWlock = merchantLock.writeLock();
private Lock lock = new MonitoredReentrantLock(MonitoredLockType.CHANNEL, true);
public Channel(final int world, final int channel) {
public Channel(final int world, final int channel, long startTime) {
this.world = world;
this.channel = channel;
this.ongoingStartTime = startTime + 10000; // rude approach to a world's last channel boot time, placeholder for the 1st wedding reservation ever
this.mapFactory = new MapleMapFactory(null, MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/Map.wz")), MapleDataProviderFactory.getDataProvider(new File(System.getProperty("wzpath") + "/String.wz")), world, channel);
try {
eventSM = new EventScriptManager(this, getEvents());
@@ -528,4 +547,297 @@ public final class Channel {
lock.unlock();
}
}
public Pair<Boolean, Pair<Integer, Set<Integer>>> getNextWeddingReservation(boolean cathedral) {
Integer ret;
lock.lock();
try {
List<Integer> weddingReservationQueue = (cathedral ? cathedralReservationQueue : chapelReservationQueue);
if(weddingReservationQueue.isEmpty()) return null;
ret = weddingReservationQueue.remove(0);
if(ret == null) return null;
} finally {
lock.unlock();
}
World wserv = Server.getInstance().getWorld(world);
Pair<Integer, Integer> coupleId = wserv.getMarriageQueuedCouple(ret);
Pair<Boolean, Set<Integer>> typeGuests = wserv.removeMarriageQueued(ret);
Pair<String, String> couple = new Pair<>(MapleCharacter.getNameById(coupleId.getLeft()), MapleCharacter.getNameById(coupleId.getRight()));
wserv.dropMessage(6, "[WEDDING] " + couple.getLeft() + " and " + couple.getRight() + "'s wedding will start soon, and it will be held at the " + (cathedral ? "Cathedral" : "Chapel") + " in Amoria, on channel " + channel + ". Cheers for their love!!");
return new Pair<>(typeGuests.getLeft(), new Pair<>(ret, typeGuests.getRight()));
}
public boolean isWeddingReserved(Integer weddingId) {
World wserv = Server.getInstance().getWorld(world);
lock.lock();
try {
return wserv.isMarriageQueued(weddingId) || weddingId.equals(ongoingCathedral) || weddingId.equals(ongoingChapel);
} finally {
lock.unlock();
}
}
public int getWeddingReservationStatus(Integer weddingId, boolean cathedral) {
if(weddingId == null) return -1;
lock.lock();
try {
if(cathedral) {
if(weddingId.equals(ongoingCathedral)) return 0;
for(int i = 0; i < cathedralReservationQueue.size(); i++) {
if(weddingId.equals(cathedralReservationQueue.get(i))) {
return i + 1;
}
}
} else {
if(weddingId.equals(ongoingChapel)) return 0;
for(int i = 0; i < chapelReservationQueue.size(); i++) {
if(weddingId.equals(chapelReservationQueue.get(i))) {
return i + 1;
}
}
}
return -1;
} finally {
lock.unlock();
}
}
public int pushWeddingReservation(Integer weddingId, boolean cathedral, boolean premium, Integer groomId, Integer brideId) {
if(weddingId == null || isWeddingReserved(weddingId)) return -1;
World wserv = Server.getInstance().getWorld(world);
wserv.putMarriageQueued(weddingId, cathedral, premium, groomId, brideId);
lock.lock();
try {
List<Integer> weddingReservationQueue = (cathedral ? cathedralReservationQueue : chapelReservationQueue);
int delay = ServerConstants.WEDDING_RESERVATION_DELAY - 1 - weddingReservationQueue.size();
for(int i = 0; i < delay; i++) {
weddingReservationQueue.add(null); // push empty slots to fill the waiting time
}
weddingReservationQueue.add(weddingId);
return weddingReservationQueue.size();
} finally {
lock.unlock();
}
}
public boolean isOngoingWeddingGuest(boolean cathedral, int playerId) {
lock.lock();
try {
if(cathedral) {
return ongoingCathedralGuests != null && ongoingCathedralGuests.contains(playerId);
} else {
return ongoingChapelGuests != null && ongoingChapelGuests.contains(playerId);
}
} finally {
lock.unlock();
}
}
public Integer getOngoingWedding(boolean cathedral) {
lock.lock();
try {
return cathedral ? ongoingCathedral : ongoingChapel;
} finally {
lock.unlock();
}
}
public Boolean getOngoingWeddingType(boolean cathedral) {
lock.lock();
try {
return cathedral ? ongoingCathedralType : ongoingChapelType;
} finally {
lock.unlock();
}
}
public void closeOngoingWedding(boolean cathedral) {
lock.lock();
try {
if(cathedral) {
ongoingCathedral = null;
ongoingCathedralType = null;
ongoingCathedralGuests = null;
} else {
ongoingChapel = null;
ongoingChapelType = null;
ongoingChapelGuests = null;
}
} finally {
lock.unlock();
}
}
public void setOngoingWedding(final boolean cathedral, Boolean premium, Integer weddingId, Set<Integer> guests) {
lock.lock();
try {
if(cathedral) {
ongoingCathedral = weddingId;
ongoingCathedralType = premium;
ongoingCathedralGuests = guests;
} else {
ongoingChapel = weddingId;
ongoingChapelType = premium;
ongoingChapelGuests = guests;
}
} finally {
lock.unlock();
}
ongoingStartTime = System.currentTimeMillis();
if(weddingId != null) {
ScheduledFuture<?> weddingTask = TimerManager.getInstance().schedule(new Runnable() {
@Override
public void run() {
closeOngoingWedding(cathedral);
}
}, ServerConstants.WEDDING_RESERVATION_TIMEOUT * 60 * 1000);
if(cathedral) {
cathedralReservationTask = weddingTask;
} else {
chapelReservationTask = weddingTask;
}
}
}
public synchronized boolean acceptOngoingWedding(final boolean cathedral) { // couple succeeded to show up and started the ceremony
if(cathedral) {
if(cathedralReservationTask == null) return false;
cathedralReservationTask.cancel(false);
cathedralReservationTask = null;
} else {
if(chapelReservationTask == null) return false;
chapelReservationTask.cancel(false);
chapelReservationTask = null;
}
return true;
}
private static String getTimeLeft(long futureTime) {
StringBuilder str = new StringBuilder();
long leftTime = futureTime - System.currentTimeMillis();
if(leftTime < 0) {
return null;
}
byte mode = 0;
if(leftTime / (60*1000) > 0) {
mode++; //counts minutes
if(leftTime / (60*60*1000) > 0)
mode++; //counts hours
}
switch(mode) {
case 2:
int hours = (int) ((leftTime / (1000*60*60)));
str.append(hours + " hours, ");
case 1:
int minutes = (int) ((leftTime / (1000*60)) % 60);
str.append(minutes + " minutes, ");
default:
int seconds = (int) (leftTime / 1000) % 60 ;
str.append(seconds + " seconds");
}
return str.toString();
}
public long getWeddingTicketExpireTime(int resSlot) {
return ongoingStartTime + getRelativeWeddingTicketExpireTime(resSlot);
}
public static long getRelativeWeddingTicketExpireTime(int resSlot) {
return (resSlot * ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000);
}
public String getWeddingReservationTimeLeft(Integer weddingId) {
if(weddingId == null) return null;
lock.lock();
try {
boolean cathedral = true;
int resStatus;
resStatus = getWeddingReservationStatus(weddingId, true);
if(resStatus < 0) {
cathedral = false;
resStatus = getWeddingReservationStatus(weddingId, false);
if(resStatus < 0) {
return null;
}
}
String venue = (cathedral ? "Cathedral" : "Chapel");
if(resStatus == 0) {
return venue + " - RIGHT NOW";
}
return venue + " - " + getTimeLeft(ongoingStartTime + (resStatus * ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000)) + " from now";
} finally {
lock.unlock();
}
}
public Pair<Integer, Integer> getWeddingCoupleForGuest(int guestId, boolean cathedral) {
lock.lock();
try {
return (isOngoingWeddingGuest(cathedral, guestId)) ? Server.getInstance().getWorld(world).getRelationshipCouple(getOngoingWedding(cathedral)) : null;
} finally {
lock.unlock();
}
}
public void dropMessage(int type, String message) {
for (MapleCharacter player : getPlayerStorage().getAllCharacters()) {
player.dropMessage(type, message);
}
}
public void debugMarriageStatus() {
System.out.println(" ----- WORLD DATA -----");
Server.getInstance().getWorld(world).debugMarriageStatus();
System.out.println(" ----- CH. " + channel + " -----");
System.out.println(" ----- CATHEDRAL -----");
System.out.println("Current Queue: " + cathedralReservationQueue);
System.out.println("Cancel Task: " + (cathedralReservationTask != null));
System.out.println("Ongoing wid: " + ongoingCathedral);
System.out.println();
System.out.println("Ongoing wid: " + ongoingCathedral + " isPremium: " + ongoingCathedralType);
System.out.println("Guest list: " + ongoingCathedralGuests);
System.out.println();
System.out.println(" ----- CHAPEL -----");
System.out.println("Current Queue: " + chapelReservationQueue);
System.out.println("Cancel Task: " + (chapelReservationTask != null));
System.out.println("Ongoing wid: " + ongoingChapel);
System.out.println();
System.out.println("Ongoing wid: " + ongoingChapel + " isPremium: " + ongoingChapelType);
System.out.println("Guest list: " + ongoingChapelGuests);
System.out.println();
System.out.println("Starttime: " + ongoingStartTime);
}
}

View File

@@ -204,11 +204,7 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
Equip equip = (Equip) item;
if(equip.getRingId() >= 0) {
MapleRing ring = MapleRing.loadFromDb(equip.getRingId());
if (ring.getItemId() > 1112012) {
chr.addFriendshipRing(ring);
} else {
chr.addCrushRing(ring);
}
chr.addPlayerRing(ring);
}
}
}
@@ -281,8 +277,8 @@ public final class CashOperationHandler extends AbstractMaplePacketHandler {
}
c.announce(MaplePacketCreator.showCash(c.getPlayer()));
} else if (action == 0x23) { //Friendship :3
slea.readInt(); //Birthday
// if (checkBirthday(c, birthday)) {
slea.readInt(); //Birthday
// if (checkBirthday(c, birthday)) {
int payment = slea.readByte();
slea.skip(3); //0s
int snID = slea.readInt();

View File

@@ -60,6 +60,7 @@ public class EnterCashShopHandler extends AbstractMaplePacketHandler {
mc.unregisterChairBuff();
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(mc.getId(), mc.getAllBuffs());
mc.setAwayFromWorld(true);
mc.notifyMapTransferToPartner(-1);
mc.cancelAllBuffs(true);
mc.cancelBuffExpireTask();
mc.cancelDiseaseExpireTask();

View File

@@ -69,6 +69,7 @@ public final class EnterMTSHandler extends AbstractMaplePacketHandler {
chr.unregisterChairBuff();
Server.getInstance().getPlayerBuffStorage().addBuffsToStorage(chr.getId(), chr.getAllBuffs());
chr.setAwayFromWorld(true);
chr.notifyMapTransferToPartner(-1);
chr.cancelAllBuffs(true);
chr.cancelBuffExpireTask();
chr.cancelDiseaseExpireTask();

View File

@@ -51,7 +51,7 @@ public final class GeneralChatHandler extends net.AbstractMaplePacketHandler {
String[] sp = s.split(" ");
sp[0] = sp[0].toLowerCase().substring(1);
if(Commands.executeSolaxiaPlayerCommand(c, sp, heading)) {
if(Commands.executeHeavenMsPlayerCommand(c, sp, heading)) {
String command = "";
for (String used : sp) {
command += used + " ";

View File

@@ -54,6 +54,7 @@ import constants.GameConstants;
import constants.ServerConstants;
import java.util.Collections;
import java.util.Comparator;
import tools.packets.Wedding;
public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
@@ -255,6 +256,16 @@ public final class PlayerLoggedinHandler extends AbstractMaplePacketHandler {
player.updateCouponRates();
player.receivePartyMemberHP();
if(player.getPartnerId() > 0) {
int partnerId = player.getPartnerId();
final MapleCharacter partner = c.getWorldServer().getPlayerStorage().getCharacterById(partnerId);
if(partner != null && !partner.isAwayFromWorld()) {
player.announce(Wedding.OnNotifyWeddingPartnerTransfer(partnerId, partner.getMapId()));
partner.announce(Wedding.OnNotifyWeddingPartnerTransfer(player.getId(), player.getMapId()));
}
}
}
private static void showDueyNotification(MapleClient c, MapleCharacter player) {

View File

@@ -25,86 +25,477 @@ package net.server.channel.handlers;
//import java.sql.PreparedStatement;
import client.MapleClient;
import client.MapleCharacter;
import client.inventory.MapleInventoryType;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
//import tools.DatabaseConnection;
import net.AbstractMaplePacketHandler;
import server.MapleInventoryManipulator;
import tools.DatabaseConnection;
import tools.data.input.SeekableLittleEndianAccessor;
//import scripting.npc.NPCScriptManager;
import tools.Pair;
import tools.MaplePacketCreator;
import tools.packets.Wedding;
import net.server.world.World;
import net.server.channel.Channel;
import server.MapleItemInformationProvider;
import client.MapleRing;
import client.inventory.Equip;
import client.inventory.Item;
/**
* @author Jvlaple
* @author Ronan (major overhaul on Ring handling mechanics)
*/
public final class RingActionHandler extends AbstractMaplePacketHandler {
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
byte mode = slea.readByte();
MapleCharacter player = c.getPlayer();
switch (mode) {
case 0: //Send
String partnerName = slea.readMapleAsciiString();
MapleCharacter partner = c.getChannelServer().getPlayerStorage().getCharacterByName(partnerName);
if (partnerName.equalsIgnoreCase(player.getName())) {
c.getPlayer().dropMessage(1, "You cannot put your own name in it.");
return;
} else if (partner == null) {
c.getPlayer().dropMessage(1, partnerName + " was not found on this channel. If you are both logged in, please make sure you are in the same channel.");
return;
} else if (partner.getGender() == player.getGender()) {
c.getPlayer().dropMessage(1, "Your partner is the same gender as you.");
return;
} //else if (player.isMarried() && partner.isMarried())
// NPCScriptManager.getInstance().start(partner.getClient(), 9201002, "marriagequestion", player);
break;
case 1: //Cancel send
c.getPlayer().dropMessage(1, "You've cancelled the request.");
boolean accepted = slea.readByte() > 0;
String proposerName = slea.readMapleAsciiString();
if (accepted) {
c.announce(MaplePacketCreator.sendEngagementRequest(proposerName));
}
break;
case 2:
slea.readByte(); //type
case 3: //Drop Ring
/*
if (player.getPartner() != null) {
try {
Connection con = DatabaseConnection.getConnection();
int pid = 0;
if (player.getGender() == 0)
pid = player.getId();
else
pid = player.getPartner().getId();//we have an engagements SQL?
PreparedStatement ps = con.prepareStatement("DELETE FROM engagements WHERE husbandid = ?");
ps.setInt(1, pid);
ps.executeUpdate();
ps.close();
ps = con.prepareStatement("UPDATE characters SET marriagequest = 0 WHERE id = ?, and WHERE id = ?");
ps.setInt(1, player.getId());
ps.setInt(2, player.getPartner().getId());
ps.executeUpdate();
ps.close();
con.close();
} catch (Exception ex) {
ex.printStackTrace();
}
c.getPlayer().dropMessage(1, "Your engagement has been broken up.");
break;
}*/
break;
case 9: // groom's wishlist
int amount = slea.readShort();
if (amount > 10) {
amount = 10;
}
String[] items = new String[10];
for (int i = 0; i < amount; i++) {
items[i] = slea.readMapleAsciiString();
}
c.announce(MaplePacketCreator.sendGroomWishlist()); //WTF<
break;
default:
System.out.println("NEW RING ACTION " + mode);
break;
private static int getBoxId(int useItemId) {
return useItemId == 2240000 ? 4031357 : (useItemId == 2240001 ? 4031359 : (useItemId == 2240002 ? 4031361 : (useItemId == 2240003 ? 4031363 : (1112300 + (useItemId - 2240004)))));
}
public static void sendEngageProposal(final MapleClient c, final String name, final int itemid) {
final int newBoxId = getBoxId(itemid);
final MapleCharacter target = c.getChannelServer().getPlayerStorage().getCharacterByName(name);
final MapleCharacter source = c.getPlayer();
// TODO: get the correct packet bytes for these popups
if (source.isMarried()) {
source.dropMessage(1, "You're already married!");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (source.getPartnerId() > 0) {
source.dropMessage(1, "You're already engaged!");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (source.getMarriageItemId() > 0) {
source.dropMessage(1, "You're already engaging someone!");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (target == null) {
source.dropMessage(1, "Unable to find " + name + " on this channel.");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (target == source) {
source.dropMessage(1, "You can't engage yourself.");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (!target.getMap().equals(source.getMap())) {
source.dropMessage(1, "Make sure your partner is on the same map!");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (!source.haveItem(itemid) || itemid < 2240000 || itemid > 2240015) {
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (target.isMarried()) {
source.dropMessage(1, "The player is already married!");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (target.getPartnerId() > 0 || target.getMarriageItemId() > 0) {
source.dropMessage(1, "The player is already engaged!");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (target.getGender() != 1) {
source.dropMessage(1, "You may only propose to a girl!");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (!MapleInventoryManipulator.checkSpace(c, newBoxId, 1, "")) {
source.dropMessage(5, "You don't have a ETC slot available right now!");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
} else if (!MapleInventoryManipulator.checkSpace(target.getClient(), newBoxId + 1, 1, "")) {
source.dropMessage(5, "The girl you proposed doesn't have a ETC slot available right now.");
source.announce(Wedding.OnMarriageResult((byte) 0));
return;
}
source.setMarriageItemId(itemid);
target.announce(Wedding.OnMarriageRequest(source.getName(), source.getId()));
}
private static void eraseEngagementOffline(int characterId) {
try {
Connection con = DatabaseConnection.getConnection();
eraseEngagementOffline(characterId, con);
con.close();
} catch(SQLException sqle) {
sqle.printStackTrace();
}
}
private static void eraseEngagementOffline(int characterId, Connection con) throws SQLException {
PreparedStatement ps = con.prepareStatement("UPDATE characters SET marriageItemId=-1, partnerId=-1 WHERE id=?");
ps.setInt(1, characterId);
ps.executeUpdate();
ps.close();
}
private static void breakEngagementOffline(int characterId) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("SELECT marriageItemId FROM characters WHERE id=?");
ps.setInt(1, characterId);
ResultSet rs = ps.executeQuery();
if (rs.next()) {
int marriageItemId = rs.getInt("marriageItemId");
if (marriageItemId > 0) {
PreparedStatement ps2 = con.prepareStatement("UPDATE inventoryitems SET expiration=0 WHERE itemid=? AND characterid=?");
ps2.setInt(1, marriageItemId);
ps2.setInt(2, characterId);
ps2.executeUpdate();
ps2.close();
}
}
rs.close();
ps.close();
eraseEngagementOffline(characterId, con);
con.close();
} catch (SQLException ex) {
System.out.println("Error updating offline breakup " + ex.getMessage());
}
}
private synchronized static void breakMarriage(MapleCharacter chr) {
int partnerid = chr.getPartnerId();
if(partnerid <= 0) return;
chr.getClient().getWorldServer().deleteRelationship(chr.getId(), partnerid);
MapleRing.removeRing(chr.getMarriageRing());
MapleCharacter partner = chr.getClient().getWorldServer().getPlayerStorage().getCharacterById(partnerid);
if(partner == null) {
eraseEngagementOffline(partnerid);
} else {
partner.dropMessage(5, chr.getName() + " has decided to break up the marriage.");
//partner.announce(Wedding.OnMarriageResult((byte) 0)); ok, how to gracefully unengage someone without the need to cc?
resetRingId(partner);
partner.setPartnerId(-1);
partner.setMarriageItemId(-1);
partner.addMarriageRing(null);
}
chr.dropMessage(5, "You have successfully break the marriage with " + MapleCharacter.getNameById(partnerid) + ".");
//chr.announce(Wedding.OnMarriageResult((byte) 0));
resetRingId(chr);
chr.setPartnerId(-1);
chr.setMarriageItemId(-1);
chr.addMarriageRing(null);
}
private static void resetRingId(MapleCharacter player) {
int ringitemid = player.getMarriageRing().getItemId();
Item it = player.getInventory(MapleInventoryType.EQUIP).findById(ringitemid);
if(it == null) {
it = player.getInventory(MapleInventoryType.EQUIPPED).findById(ringitemid);
}
if(it != null) {
Equip eqp = (Equip) it;
eqp.setRingId(-1);
}
}
private synchronized static void breakEngagement(MapleCharacter chr) {
int partnerid = chr.getPartnerId();
int marriageitemid = chr.getMarriageItemId();
chr.getClient().getWorldServer().deleteRelationship(chr.getId(), partnerid);
MapleCharacter partner = chr.getClient().getWorldServer().getPlayerStorage().getCharacterById(partnerid);
if(partner == null) {
breakEngagementOffline(partnerid);
} else {
partner.dropMessage(5, chr.getName() + " has decided to break up the engagement.");
int partnerMarriageitemid = marriageitemid + ((chr.getGender() == 0) ? 1 : -1);
if(partner.haveItem(partnerMarriageitemid)) {
MapleInventoryManipulator.removeById(partner.getClient(), MapleInventoryType.ETC, partnerMarriageitemid, (short) 1, false, false);
}
//partner.announce(Wedding.OnMarriageResult((byte) 0)); ok, how to gracefully unengage someone without the need to cc?
partner.setPartnerId(-1);
partner.setMarriageItemId(-1);
}
if(chr.haveItem(marriageitemid)) {
MapleInventoryManipulator.removeById(chr.getClient(), MapleInventoryType.ETC, marriageitemid, (short) 1, false, false);
}
chr.dropMessage(5, "You have successfully break the engagement with " + MapleCharacter.getNameById(partnerid) + ".");
//chr.announce(Wedding.OnMarriageResult((byte) 0));
chr.setPartnerId(-1);
chr.setMarriageItemId(-1);
}
public static void breakMarriageRing(MapleCharacter chr, final int wItemId) {
final MapleInventoryType type = MapleInventoryType.getByType((byte) (wItemId / 1000000));
final Item wItem = chr.getInventory(type).findById(wItemId);
final boolean weddingToken = (wItem != null && type == MapleInventoryType.ETC && wItemId / 10000 == 403);
final boolean weddingRing = (wItem != null && wItemId / 10 == 111280);
if (weddingRing) {
if(chr.getPartnerId() > 0) {
breakMarriage(chr);
}
chr.getMap().disappearingItemDrop(chr, chr, wItem, chr.getPosition());
} else if (weddingToken) {
if (chr.getPartnerId() > 0) {
breakEngagement(chr);
}
chr.getMap().disappearingItemDrop(chr, chr, wItem, chr.getPosition());
}
}
public static void giveMarriageRings(MapleCharacter player, MapleCharacter partner, int marriageRingId) {
int ringid = MapleRing.createRing(marriageRingId, player, partner);
MapleItemInformationProvider ii = MapleItemInformationProvider.getInstance();
Item ringObj = ii.getEquipById(marriageRingId);
Equip ringEqp = (Equip) ringObj;
ringEqp.setRingId(ringid);
player.addMarriageRing(MapleRing.loadFromDb(ringid));
MapleInventoryManipulator.addFromDrop(player.getClient(), ringEqp, false, -1);
player.broadcastMarriageMessage();
ringObj = ii.getEquipById(marriageRingId);
ringEqp = (Equip) ringObj;
ringEqp.setRingId(ringid + 1);
partner.addMarriageRing(MapleRing.loadFromDb(ringid + 1));
MapleInventoryManipulator.addFromDrop(partner.getClient(), ringEqp, false, -1);
partner.broadcastMarriageMessage();
}
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
byte mode = slea.readByte();
String name;
byte slot;
switch(mode) {
case 0: // Send Proposal
sendEngageProposal(c, slea.readMapleAsciiString(), slea.readInt());
break;
case 1: // Cancel Proposal
System.out.println("Cancel Ring Action: " + slea.toString()); // log packet and see if any bytes
if(c.getPlayer().getMarriageItemId() / 1000000 != 4) {
c.getPlayer().setMarriageItemId(-1);
}
break;
case 2: // Accept/Deny Proposal
final boolean accepted = slea.readByte() > 0;
name = slea.readMapleAsciiString();
final int id = slea.readInt();
final MapleCharacter source = c.getWorldServer().getPlayerStorage().getCharacterByName(name);
final MapleCharacter target = c.getPlayer();
if (source == null) {
target.announce(MaplePacketCreator.enableActions());
return;
}
final int itemid = source.getMarriageItemId();
if (target.getPartnerId() > 0 || source.getId() != id || itemid <= 0 || !source.haveItem(itemid) || source.getPartnerId() > 0 || !source.isAlive() || !target.isAlive()) {
target.announce(MaplePacketCreator.enableActions());
return;
}
if (accepted) {
final int newItemId = getBoxId(itemid);
if (!MapleInventoryManipulator.checkSpace(c, newItemId, 1, "") || !MapleInventoryManipulator.checkSpace(source.getClient(), newItemId, 1, "")) {
target.announce(MaplePacketCreator.enableActions());
return;
}
try {
MapleInventoryManipulator.removeById(source.getClient(), MapleInventoryType.USE, itemid, 1, false, false);
int marriageId = c.getWorldServer().createRelationship(source.getId(), target.getId());
source.setPartnerId(target.getId()); // engage them (new marriageitemid, partnerid for both)
target.setPartnerId(source.getId());
source.setMarriageItemId(newItemId);
target.setMarriageItemId(newItemId + 1);
MapleInventoryManipulator.addById(source.getClient(), newItemId, (short) 1);
MapleInventoryManipulator.addById(c, (newItemId + 1), (short) 1);
source.announce(Wedding.OnMarriageResult(marriageId, source, false));
target.announce(Wedding.OnMarriageResult(marriageId, source, false));
source.announce(Wedding.OnNotifyWeddingPartnerTransfer(target.getId(), target.getMapId()));
target.announce(Wedding.OnNotifyWeddingPartnerTransfer(source.getId(), source.getMapId()));
} catch (Exception e) {
System.out.println("Error with engagement " + e.getMessage());
}
} else {
source.dropMessage(1, "She has politely declined your engagement request.");
source.announce(Wedding.OnMarriageResult((byte) 0));
source.setMarriageItemId(-1);
}
break;
case 3: // Break Engagement
breakMarriageRing(c.getPlayer(), slea.readInt());
break;
case 5: // Invite %s to Wedding
name = slea.readMapleAsciiString();
int marriageId = slea.readInt();
slot = slea.readByte(); // this is an int
int itemId;
try {
itemId = c.getPlayer().getInventory(MapleInventoryType.ETC).getItem(slot).getItemId();
} catch(NullPointerException npe) {
c.announce(MaplePacketCreator.enableActions());
return;
}
if((itemId != 4031377 && itemId != 4031395) || !c.getPlayer().haveItem(itemId)) {
c.announce(MaplePacketCreator.enableActions());
return;
}
String groom = c.getPlayer().getName(), bride = MapleCharacter.getNameById(c.getPlayer().getPartnerId());
int guest = MapleCharacter.getIdByName(name);
if (groom == null || bride == null || groom.equals("") || bride.equals("") || guest <= 0) {
c.getPlayer().dropMessage(5, "Unable to find " + name + "!");
return;
}
try {
World wserv = c.getWorldServer();
Pair<Boolean, Boolean> registration = wserv.getMarriageQueuedLocation(marriageId);
if(registration != null) {
if(wserv.addMarriageGuest(marriageId, guest)) {
boolean cathedral = registration.getLeft();
int newItemId = cathedral ? 4031407 : 4031406;
Channel cserv = c.getChannelServer();
int resStatus = cserv.getWeddingReservationStatus(marriageId, cathedral);
if(resStatus > 0) {
long expiration = cserv.getWeddingTicketExpireTime(resStatus + 1);
MapleCharacter guestChr = c.getWorldServer().getPlayerStorage().getCharacterById(guest);
if(guestChr != null && MapleInventoryManipulator.checkSpace(guestChr.getClient(), newItemId, 1, "") && MapleInventoryManipulator.addById(guestChr.getClient(), newItemId, (short) 1, expiration)) {
guestChr.dropMessage(6, "[WEDDING] You've been invited to " + groom + " and " + bride + "'s Wedding!");
} else {
c.getPlayer().sendNote(name, "You've been invited to " + groom + " and " + bride + "'s Wedding! Receive your invitation from Duey!", (byte) 0);
Item weddingTicket = new Item(newItemId, (short) 0, (short) 1);
weddingTicket.setExpiration(expiration);
DueyHandler.addItemToDB(weddingTicket, 1, 0, groom, guest);
}
} else {
c.getPlayer().dropMessage(5, "Wedding is already under way. You cannot invite any more guests for the event.");
}
} else {
c.getPlayer().dropMessage(5, "'" + name + "' is already invited for your marriage.");
}
} else {
c.getPlayer().dropMessage(5, "Invitation was not sent to '" + name + "'. Either the time for your marriage reservation already came or it was not found.");
}
} catch (SQLException ex) {
ex.printStackTrace();
return;
}
c.getAbstractPlayerInteraction().gainItem(itemId, (short) -1);
break;
case 6: // Open Wedding Invitation
slot = (byte) slea.readInt();
int invitationid = slea.readInt();
if(invitationid == 4031406 || invitationid == 4031407) {
Item item = c.getPlayer().getInventory(MapleInventoryType.ETC).getItem(slot);
if(item == null || item.getItemId() != invitationid) {
c.announce(MaplePacketCreator.enableActions());
return;
}
// collision case: most soon-to-come wedding will show up
Pair<Integer, Integer> coupleId = c.getWorldServer().getWeddingCoupleForGuest(c.getPlayer().getId(), invitationid == 4031407);
if (coupleId != null) {
int groomId = coupleId.getLeft(), brideId = coupleId.getRight();
c.announce(Wedding.sendWeddingInvitation(MapleCharacter.getNameById(groomId), MapleCharacter.getNameById(brideId)));
}
}
break;
case 9: // Groom and Bride's Wishlist
short size = slea.readShort();
List<String> itemnames = new ArrayList<>(size);
for (int i = 0; i < size; i++) {
itemnames.add(slea.readMapleAsciiString());
}
System.out.println("G&B WISHLIST: " + itemnames);
/*
if (c.getPlayer().getMarriageItemId() > -1) {
switch(c.getPlayer().getMarriageItemId()) {
case 10: // Premium Cathedral
c.getAbstractPlayerInteraction().gainItem(4031375, (short)1);
c.getAbstractPlayerInteraction().gainItem(4031395, (short)15);
break;
case 11: // Normal Cathedral
c.getAbstractPlayerInteraction().gainItem(4031480, (short)1);
c.getAbstractPlayerInteraction().gainItem(4031395, (short)15);
break;
case 20: // Premium Chapel
c.getAbstractPlayerInteraction().gainItem(4031376, (short)1);
c.getAbstractPlayerInteraction().gainItem(4031377, (short)15);
break;
case 21: // Normal Chapel
c.getAbstractPlayerInteraction().gainItem(4031481, (short)1);
c.getAbstractPlayerInteraction().gainItem(4031377, (short)15);
break;
default: {
System.out.println("Invalid Wedding Type for player " + c.getPlayer().getName() + "!");
break;
}
}
//c.getPlayer().setMarriageItemId(-1); ?????
}
if (c.getPlayer().getWishlist() == null) {
c.getPlayer().registerWishlist(itemnames);
}
if (c.getPlayer().getWedding() != null) {
if (c.getPlayer().getGender() == 0 ? c.getPlayer().getWedding().isExistantGroom(c.getPlayer().getId()) : c.getPlayer().getWedding().isExistantBride(c.getPlayer().getId())) {
c.getPlayer().getWedding().registerWishlist(c.getPlayer().getGender() == 1, itemnames);
}
}
*/
break;
default:
System.out.println("Unhandled RING_ACTION Mode: " + slea.toString());
break;
}
c.getSession().write(MaplePacketCreator.enableActions());
}
}

View File

@@ -21,36 +21,29 @@
*/
package net.server.channel.handlers;
//import client.MapleCharacter;
import client.MapleCharacter;
import client.MapleClient;
//import client.command.CommandProcessor;
import net.AbstractMaplePacketHandler;
//import tools.MaplePacketCreator;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
public final class SpouseChatHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
System.out.println(slea.toString());
// slea.readMapleAsciiString();//recipient
// String msg = slea.readMapleAsciiString();
// if (!CommandProcessor.processCommand(c, msg))
// if (c.getPlayer().isMarried()) {
// MapleCharacter wife = c.getChannelServer().getPlayerStorage().getCharacterById(c.getPlayer().getPartnerId());
// if (wife != null) {
// wife.getClient().announce(MaplePacketCreator.sendSpouseChat(c.getPlayer(), msg));
// c.announce(MaplePacketCreator.sendSpouseChat(c.getPlayer(), msg));
// } else
// try {
// if (c.getChannelServer().getWorldInterface().isConnected(wife.getName())) {
// c.getChannelServer().getWorldInterface().sendSpouseChat(c.getPlayer().getName(), wife.getName(), msg);
// c.announce(MaplePacketCreator.sendSpouseChat(c.getPlayer(), msg));
// } else
// c.getPlayer().message("You are either not married or your spouse is currently offline.");
// } catch (Exception e) {
// e.printStackTrace();
// c.getPlayer().message("You are either not married or your spouse is currently offline.");
// c.getChannelServer().reconnectWorld();
// }
// }
slea.readMapleAsciiString();//recipient
String msg = slea.readMapleAsciiString();
int partnerId = c.getPlayer().getPartnerId();
if (partnerId > 0) { // yay marriage
MapleCharacter spouse = c.getWorldServer().getPlayerStorage().getCharacterById(partnerId);
if (spouse != null) {
spouse.announce(MaplePacketCreator.OnCoupleMessage(c.getPlayer().getName(), msg, true));
c.announce(MaplePacketCreator.OnCoupleMessage(c.getPlayer().getName(), msg, true));
} else {
c.getPlayer().dropMessage(5, "Your spouse is currently offline.");
}
} else {
c.getPlayer().dropMessage(5, "You don't have a spouse.");
}
}
}

View File

@@ -1,39 +1,93 @@
/*
* To change this template, choose Tools | Templates
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package net.server.channel.handlers;
import client.MapleCharacter;
import client.MapleClient;
import client.inventory.Item;
import client.inventory.MapleInventoryType;
import client.MapleCharacter;
import client.MapleClient;
import constants.ItemConstants;
import tools.DatabaseConnection;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import net.AbstractMaplePacketHandler;
import server.MapleInventoryManipulator;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.packets.Wedding;
/**
*
* @author Kevin
* @author Eric
*/
public class WeddingHandler extends AbstractMaplePacketHandler {
@Override
public void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
//System.out.println("Wedding Packet: " + slea);
MapleCharacter chr = c.getPlayer();
byte operation = slea.readByte();
switch (operation) {
case 0x06://Add an item to the Wedding Registry
short slot = slea.readShort();
int itemid = slea.readInt();
short quantity = slea.readShort();
MapleInventoryType type = ItemConstants.getInventoryType(itemid);
Item item = chr.getInventory(type).getItem(slot);
if (itemid == item.getItemId() && quantity <= item.getQuantity()) {
c.announce(MaplePacketCreator.addItemToWeddingRegistry(chr, item));
public final class WeddingHandler extends AbstractMaplePacketHandler {
/*
public static final void OnWeddingProgress(byte action, MapleClient c) {
// -- Pelvis Bebop:
// 0x00: "We are gathered here today..."
// 0x01: "Very well! I pronounce you..."
// 0x02: "You two truly are a sight to..."
// 0x03: Wedding Ceremony Ended, initialize the Wedding Effect upon the two married characters
// -- High Priest John: (Unknown action bytes)
// 0x00: " "
// 0x01: " "
// 0x02: "Do you wish to bless this couple?..."
// 0x03: Wedding Ceremony Ended, initialize the Wedding Effect upon the two married characters
if (c.getPlayer().getWedding() != null) {
if (c.getPlayer().getGender() == 0 ? c.getPlayer().getWedding().isExistantGroom(c.getPlayer().getId()) : c.getPlayer().getWedding().isExistantBride(c.getPlayer().getId())) {
c.getPlayer().getMap().broadcastMessage(Wedding.OnWeddingProgress(action == 2, c.getPlayer().getId(), c.getPlayer().getPartnerId(), (byte)(action+1)));
c.getPlayer().getWedding().incrementStage();
c.getPlayer().getPartner().getWedding().incrementStage(); // pls don't b a bitch and throw npe ):<
if (action == 2) {
c.getPlayer().setMarried(true);
c.getChannelServer().getPlayerStorage().getCharacterById(c.getPlayer().getPartnerId()).setMarried(true);
}
}
}
c.announce(MaplePacketCreator.enableActions());
}
public static final void OnWeddingGiftResult(SeekableLittleEndianAccessor slea, MapleClient c) {
System.out.println("New WEDDING_GIFT_RESULT: " + slea.toString());
byte mode = slea.readByte();
switch(mode) {
case 0x06: // "SEND ITEM"
short slot = slea.readShort(); // isn't this a byte? o.O
int itemId = slea.readInt();
short quantity = slea.readShort();
if (c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).getItem((byte)slot).getItemId() == itemId && c.getPlayer().getInventory(InventoryConstants.getInventoryType(itemId)).getItem((byte)slot).getQuantity() >= quantity) {
if (c.getPlayer().getWedding() == null) {
c.getPlayer().startWedding(); // TODO
}
List<String> itemnames = new ArrayList<>();
Item item = c.getPlayer().getInventory(ItemConstants.getInventoryType(itemId)).getItem((byte)slot);
boolean bride = false;
c.getPlayer().getWedding().registerWishlistItem(item, bride);
c.announce(Wedding.OnWeddingGiftResult((byte)11, itemnames, c.getPlayer().getWedding().getWishlistItems(bride))); // todo: remove item from inventory if success
}
case 0x08: // "EXIT"
if (slea.available() != 0) {
System.out.println("WEDDING_GIFT_RESULT: " + slea.toString());
}
c.announce(MaplePacketCreator.enableActions());
break;
default: {
System.out.println("Unknown Mode Found: " + mode + " : " + slea.toString());
}
}
}
}
*/
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
c.announce(MaplePacketCreator.enableActions());
}
}

View File

@@ -0,0 +1,53 @@
/*
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
Copyleft (L) 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 net.AbstractMaplePacketHandler;
import scripting.event.EventInstanceManager;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.packets.Wedding;
/**
*
* @author Ronan
*/
public final class WeddingTalkHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
byte action = slea.readByte();
if(action == 1) {
EventInstanceManager eim = c.getPlayer().getEventInstance();
if(eim != null && !(c.getPlayer().getId() == eim.getIntProperty("groomId") || c.getPlayer().getId() == eim.getIntProperty("brideId"))) {
c.announce(Wedding.OnWeddingProgress(false, 0, 0, (byte) 2));
} else {
c.announce(Wedding.OnWeddingProgress(true, 0, 0, (byte) 3));
}
} else {
c.announce(Wedding.OnWeddingProgress(true, 0, 0, (byte) 3));
}
c.announce(MaplePacketCreator.enableActions());
}
}

View File

@@ -0,0 +1,47 @@
/*
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
Copyleft (L) 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 net.AbstractMaplePacketHandler;
import scripting.event.EventInstanceManager;
import tools.MaplePacketCreator;
import tools.data.input.SeekableLittleEndianAccessor;
import tools.packets.Wedding;
/**
*
* @author Ronan
*/
public final class WeddingTalkMoreHandler extends AbstractMaplePacketHandler {
@Override
public final void handlePacket(SeekableLittleEndianAccessor slea, MapleClient c) {
EventInstanceManager eim = c.getPlayer().getEventInstance();
if(eim != null && !(c.getPlayer().getId() == eim.getIntProperty("groomId") || c.getPlayer().getId() == eim.getIntProperty("brideId"))) {
eim.gridInsert(c.getPlayer(), 1);
c.getPlayer().dropMessage(5, "High Priest John: Your blessings have been added to their love. What a noble act for a lovely couple!");
}
c.announce(Wedding.OnWeddingProgress(true, 0, 0, (byte) 3));
c.announce(MaplePacketCreator.enableActions());
}
}

View File

@@ -0,0 +1,56 @@
/*
This file is part of the HeavenMS (MapleSolaxiaV2) MapleStory Server
Copyleft (L) 2017 RonanLana
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation version 3 as published by
the Free Software Foundation. You may not use, modify or distribute
this program under any other version of the GNU Affero General Public
License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package net.server.worker;
import java.util.Set;
import net.server.world.World;
import net.server.channel.Channel;
import tools.Pair;
/**
* @author Ronan
*/
public class WeddingReservationWorker extends BaseWorker implements Runnable {
@Override
public void run() {
for(Channel ch : wserv.getChannels()) {
Pair<Boolean, Pair<Integer, Set<Integer>>> wedding;
wedding = ch.getNextWeddingReservation(true); // start cathedral
if(wedding != null) {
ch.setOngoingWedding(true, wedding.getLeft(), wedding.getRight().getLeft(), wedding.getRight().getRight());
} else {
ch.setOngoingWedding(true, null, null, null);
}
wedding = ch.getNextWeddingReservation(false); // start chapel
if(wedding != null) {
ch.setOngoingWedding(false, wedding.getLeft(), wedding.getRight().getLeft(), wedding.getRight().getRight());
} else {
ch.setOngoingWedding(false, null, null, null);
}
}
}
public WeddingReservationWorker(World world) {
super(world);
}
}

View File

@@ -31,7 +31,9 @@ import constants.ServerConstants;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
@@ -58,6 +60,7 @@ import server.maps.AbstractMapleMapObject;
import net.server.worker.CharacterAutosaverWorker;
import net.server.worker.MountTirednessWorker;
import net.server.worker.PetFullnessWorker;
import net.server.worker.WeddingReservationWorker;
import net.server.PlayerStorage;
import net.server.Server;
import net.server.channel.Channel;
@@ -83,10 +86,14 @@ public class World {
private Map<Integer, MapleMessenger> messengers = new HashMap<>();
private AtomicInteger runningMessengerId = new AtomicInteger();
private Map<Integer, MapleFamily> families = new LinkedHashMap<>();
private Map<Integer, Integer> relationships = new HashMap<>();
private Map<Integer, Pair<Integer, Integer>> relationshipCouples = new HashMap<>();
private Map<Integer, MapleGuildSummary> gsStore = new HashMap<>();
private PlayerStorage players = new PlayerStorage();
private Set<Integer> queuedGuilds = new HashSet<>();
private Map<Integer, Pair<Pair<Boolean, Boolean>, Pair<Integer, Integer>>> queuedMarriages = new HashMap<>();
private Map<Integer, Set<Integer>> marriageGuests = new HashMap<>();
private Map<Integer, MapleParty> parties = new HashMap<>();
private AtomicInteger runningPartyId = new AtomicInteger();
@@ -113,6 +120,7 @@ public class World {
private long merchantUpdate;
private ScheduledFuture<?> charactersSchedule;
private ScheduledFuture<?> marriagesSchedule;
public World(int world, int flag, String eventmsg, int exprate, int droprate, int mesorate, int questrate) {
this.id = world;
@@ -128,9 +136,11 @@ public class World {
petUpdate = System.currentTimeMillis();
mountUpdate = petUpdate;
petsSchedule = TimerManager.getInstance().register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000);
mountsSchedule = TimerManager.getInstance().register(new MountTirednessWorker(this), 60 * 1000, 60 * 1000);
charactersSchedule = TimerManager.getInstance().register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000);
TimerManager tman = TimerManager.getInstance();
petsSchedule = tman.register(new PetFullnessWorker(this), 60 * 1000, 60 * 1000);
mountsSchedule = tman.register(new MountTirednessWorker(this), 60 * 1000, 60 * 1000);
charactersSchedule = tman.register(new CharacterAutosaverWorker(this), 60 * 60 * 1000, 60 * 60 * 1000);
marriagesSchedule = tman.register(new WeddingReservationWorker(this), ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000, ServerConstants.WEDDING_RESERVATION_INTERVAL * 60 * 1000);
}
public List<Channel> getChannels() {
@@ -371,6 +381,96 @@ public class World {
queuedGuilds.remove(guildId);
}
public boolean isMarriageQueued(int marriageId) {
return queuedMarriages.containsKey(marriageId);
}
public Pair<Boolean, Boolean> getMarriageQueuedLocation(int marriageId) {
Pair<Pair<Boolean, Boolean>, Pair<Integer, Integer>> qm = queuedMarriages.get(marriageId);
return (qm != null) ? qm.getLeft() : null;
}
public Pair<Integer, Integer> getMarriageQueuedCouple(int marriageId) {
Pair<Pair<Boolean, Boolean>, Pair<Integer, Integer>> qm = queuedMarriages.get(marriageId);
return (qm != null) ? qm.getRight() : null;
}
public void putMarriageQueued(int marriageId, boolean cathedral, boolean premium, int groomId, int brideId) {
queuedMarriages.put(marriageId, new Pair<>(new Pair<>(cathedral, premium), new Pair<>(groomId, brideId)));
marriageGuests.put(marriageId, new HashSet());
}
public Pair<Boolean, Set<Integer>> removeMarriageQueued(int marriageId) {
Boolean type = queuedMarriages.remove(marriageId).getLeft().getRight();
Set<Integer> guests = marriageGuests.remove(marriageId);
return new Pair<>(type, guests);
}
public synchronized boolean addMarriageGuest(int marriageId, int playerId) {
Set<Integer> guests = marriageGuests.get(marriageId);
if(guests != null) {
if(guests.contains(playerId)) return false;
guests.add(playerId);
return true;
}
return false;
}
public Pair<Integer, Integer> getWeddingCoupleForGuest(int guestId, Boolean cathedral) {
for(Channel ch : channels) {
Pair<Integer, Integer> p = ch.getWeddingCoupleForGuest(guestId, cathedral);
if(p != null) {
return p;
}
}
List<Integer> possibleWeddings = new LinkedList<>();
for(Entry<Integer, Set<Integer>> mg : new HashSet<>(marriageGuests.entrySet())) {
if(mg.getValue().contains(guestId)) {
Pair<Boolean, Boolean> loc = getMarriageQueuedLocation(mg.getKey());
if(loc != null && cathedral.equals(loc.getLeft())) {
possibleWeddings.add(mg.getKey());
}
}
}
int pwSize = possibleWeddings.size();
if(pwSize == 0) {
return null;
} else if(pwSize > 1) {
int selectedPw = -1;
int selectedPos = Integer.MAX_VALUE;
for(Integer pw : possibleWeddings) {
for(Channel ch : channels) {
int pos = ch.getWeddingReservationStatus(pw, cathedral);
if(pos != -1) {
if(pos < selectedPos) {
selectedPos = pos;
selectedPw = pw;
break;
}
}
}
}
if(selectedPw == -1) return null;
possibleWeddings.clear();
possibleWeddings.add(selectedPw);
}
return getMarriageQueuedCouple(possibleWeddings.get(0));
}
public void debugMarriageStatus() {
System.out.println("Queued marriages: " + queuedMarriages);
System.out.println("Guest list: " + marriageGuests);
}
public MapleParty createParty(MaplePartyCharacter chrfor) {
int partyid = runningPartyId.getAndIncrement();
MapleParty party = new MapleParty(partyid, chrfor);
@@ -1028,33 +1128,160 @@ public class World {
}
public List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> getAvailableItemBundles(int itemid) {
List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> hmsAvailable = new ArrayList<>();
List<Pair<MaplePlayerShopItem, AbstractMapleMapObject>> hmsAvailable = new ArrayList<>();
for (MapleHiredMerchant hm : getActiveMerchants()) {
List<MaplePlayerShopItem> itemBundles = hm.sendAvailableBundles(itemid);
for(MaplePlayerShopItem mpsi : itemBundles) {
hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) hm));
}
for (MapleHiredMerchant hm : getActiveMerchants()) {
List<MaplePlayerShopItem> itemBundles = hm.sendAvailableBundles(itemid);
for(MaplePlayerShopItem mpsi : itemBundles) {
hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) hm));
}
}
for (MaplePlayerShop ps : getActivePlayerShops()) {
List<MaplePlayerShopItem> itemBundles = ps.sendAvailableBundles(itemid);
for(MaplePlayerShopItem mpsi : itemBundles) {
hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) ps));
}
}
Collections.sort(hmsAvailable, new Comparator<Pair<MaplePlayerShopItem, AbstractMapleMapObject>>() {
@Override
public int compare(Pair<MaplePlayerShopItem, AbstractMapleMapObject> p1, Pair<MaplePlayerShopItem, AbstractMapleMapObject> p2) {
return p1.getLeft().getPrice() - p2.getLeft().getPrice();
}
});
hmsAvailable.subList(0, Math.min(hmsAvailable.size(), 200)); //truncates the list to have up to 200 elements
return hmsAvailable;
}
private void pushRelationshipCouple(Pair<Integer, Pair<Integer, Integer>> couple) {
int mid = couple.getLeft(), hid = couple.getRight().getLeft(), wid = couple.getRight().getRight();
relationshipCouples.put(mid, couple.getRight());
relationships.put(hid, mid);
relationships.put(wid, mid);
}
public Pair<Integer, Integer> getRelationshipCouple(int relationshipId) {
Pair<Integer, Integer> rc = relationshipCouples.get(relationshipId);
if(rc == null) {
Pair<Integer, Pair<Integer, Integer>> couple = getRelationshipCoupleFromDb(relationshipId, true);
if(couple == null) return null;
pushRelationshipCouple(couple);
rc = couple.getRight();
}
return rc;
}
public int getRelationshipId(int playerId) {
Integer ret = relationships.get(playerId);
if(ret == null) {
Pair<Integer, Pair<Integer, Integer>> couple = getRelationshipCoupleFromDb(playerId, false);
if(couple == null) return -1;
pushRelationshipCouple(couple);
ret = couple.getLeft();
}
return ret;
}
private static Pair<Integer, Pair<Integer, Integer>> getRelationshipCoupleFromDb(int id, boolean usingMarriageId) {
try {
Connection con = DatabaseConnection.getConnection();
Integer mid = null, hid = null, wid = null;
PreparedStatement ps;
if(usingMarriageId) {
ps = con.prepareStatement("SELECT * FROM marriages WHERE marriageid = ?");
ps.setInt(1, id);
} else {
ps = con.prepareStatement("SELECT * FROM marriages WHERE husbandid = ? OR wifeid = ?");
ps.setInt(1, id);
ps.setInt(2, id);
}
for (MaplePlayerShop ps : getActivePlayerShops()) {
List<MaplePlayerShopItem> itemBundles = ps.sendAvailableBundles(itemid);
for(MaplePlayerShopItem mpsi : itemBundles) {
hmsAvailable.add(new Pair<>(mpsi, (AbstractMapleMapObject) ps));
}
ResultSet rs = ps.executeQuery();
if(rs.next()) {
mid = rs.getInt("marriageid");
hid = rs.getInt("husbandid");
wid = rs.getInt("wifeid");
}
rs.close();
ps.close();
con.close();
return (mid == null) ? null : new Pair<>(mid, new Pair<>(hid, wid));
} catch (SQLException se) {
se.printStackTrace();
return null;
}
}
public int createRelationship(int groomId, int brideId) {
int ret = addRelationshipToDb(groomId, brideId);
pushRelationshipCouple(new Pair<>(ret, new Pair<>(groomId, brideId)));
return ret;
}
private static int addRelationshipToDb(int groomId, int brideId) {
try {
Connection con = DatabaseConnection.getConnection();
Collections.sort(hmsAvailable, new Comparator<Pair<MaplePlayerShopItem, AbstractMapleMapObject>>() {
@Override
public int compare(Pair<MaplePlayerShopItem, AbstractMapleMapObject> p1, Pair<MaplePlayerShopItem, AbstractMapleMapObject> p2) {
return p1.getLeft().getPrice() - p2.getLeft().getPrice();
}
});
PreparedStatement ps = con.prepareStatement("INSERT INTO marriages (husbandid, wifeid) VALUES (?, ?)", Statement.RETURN_GENERATED_KEYS);
ps.setInt(1, groomId);
ps.setInt(2, brideId);
ps.executeUpdate();
hmsAvailable.subList(0, Math.min(hmsAvailable.size(), 200)); //truncates the list to have up to 200 elements
return hmsAvailable;
ResultSet rs = ps.getGeneratedKeys();
rs.next();
int ret = rs.getInt(1);
rs.close();
ps.close();
con.close();
return ret;
} catch (SQLException se) {
se.printStackTrace();
return -1;
}
}
public void deleteRelationship(int playerId, int partnerId) {
int relationshipId = relationships.get(playerId);
deleteRelationshipFromDb(relationshipId);
relationshipCouples.remove(relationshipId);
relationships.remove(playerId);
relationships.remove(partnerId);
}
private static void deleteRelationshipFromDb(int playerId) {
try {
Connection con = DatabaseConnection.getConnection();
PreparedStatement ps = con.prepareStatement("DELETE FROM marriages WHERE marriageid = ?");
ps.setInt(1, playerId);
ps.executeUpdate();
ps.close();
con.close();
} catch (SQLException se) {
se.printStackTrace();
}
}
public void dropMessage(int type, String message) {
for (MapleCharacter player : getPlayerStorage().getAllCharacters()) {
player.dropMessage(type, message);
}
}
public final void shutdown() {