/* OrpheusMS: MapleStory Private Server based on OdinMS Copyright (C) 2012 Aaron Weiss <aaron@deviant-core.net> Patrick Huy <patrick.huy@frz.cc> Matthias Butz <matze@odinms.de> Jan Christian Meyer <vimes@odinms.de> This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. 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.guild; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Set; import client.MapleCharacter; import client.MapleClient; import java.util.LinkedList; import tools.DatabaseConnection; import tools.Output; import net.MaplePacket; import net.server.Channel; import net.server.Server; import tools.MaplePacketCreator; public class MapleGuild { public final static int CREATE_GUILD_COST = 1500000; public final static int CHANGE_EMBLEM_COST = 5000000; private enum BCOp { NONE, DISBAND, EMBELMCHANGE } private List<MapleGuildCharacter> members; private String rankTitles[] = new String[5]; // 1 = master, 2 = jr, 5 = // lowest member private String name, notice; private int id, gp, logo, logoColor, leader, capacity, logoBG, logoBGColor, signature, allianceId; private byte world; private Map<Byte, List<Integer>> notifications = new LinkedHashMap<Byte, List<Integer>>(); private boolean bDirty = true; public MapleGuild(MapleGuildCharacter initiator) { int guildid = initiator.getGuildId(); world = initiator.getWorld(); members = new ArrayList<MapleGuildCharacter>(); Connection con = DatabaseConnection.getConnection(); try { PreparedStatement ps = con.prepareStatement("SELECT * FROM guilds WHERE guildid = " + guildid); ResultSet rs = ps.executeQuery(); if (!rs.first()) { id = -1; ps.close(); rs.close(); return; } id = guildid; name = rs.getString("name"); gp = rs.getInt("GP"); logo = rs.getInt("logo"); logoColor = rs.getInt("logoColor"); logoBG = rs.getInt("logoBG"); logoBGColor = rs.getInt("logoBGColor"); capacity = rs.getInt("capacity"); for (int i = 1; i <= 5; i++) { rankTitles[i - 1] = rs.getString("rank" + i + "title"); } leader = rs.getInt("leader"); notice = rs.getString("notice"); signature = rs.getInt("signature"); allianceId = rs.getInt("allianceId"); ps.close(); rs.close(); ps = con.prepareStatement("SELECT id, name, level, job, guildrank, allianceRank FROM characters WHERE guildid = ? ORDER BY guildrank ASC, name ASC"); ps.setInt(1, guildid); rs = ps.executeQuery(); if (!rs.first()) { rs.close(); ps.close(); return; } do { members.add(new MapleGuildCharacter(rs.getInt("id"), rs.getInt("level"), rs.getString("name"), (byte) -1, world, rs.getInt("job"), rs.getInt("guildrank"), guildid, false, rs.getInt("allianceRank"))); } while (rs.next()); setOnline(initiator.getId(), true, initiator.getChannel()); ps.close(); rs.close(); } catch (SQLException se) { Output.print("Unable to read guild information from database.\n" + se); return; } } public void buildNotifications() { if (!bDirty) { return; } Set<Byte> chs = Server.getInstance().getChannelServer(world); if (notifications.keySet().size() != chs.size()) { notifications.clear(); for (Byte ch : chs) { notifications.put(ch, new LinkedList<Integer>()); } } else { for (List<Integer> l : notifications.values()) { l.clear(); } } synchronized (members) { for (MapleGuildCharacter mgc : members) { if (!mgc.isOnline()) { continue; } List<Integer> ch = notifications.get(mgc.getChannel()); if (ch != null) ch.add(mgc.getId()); // Unable to connect to Channel... error was here } } bDirty = false; } public void writeToDB(boolean bDisband) { try { Connection con = DatabaseConnection.getConnection(); if (!bDisband) { StringBuilder builder = new StringBuilder(); builder.append("UPDATE guilds SET GP = ?, logo = ?, logoColor = ?, logoBG = ?, logoBGColor = ?, "); for (int i = 0; i < 5; i++) { builder.append("rank").append(i + 1).append("title = ?, "); } builder.append("capacity = ?, notice = ? WHERE guildid = ?"); PreparedStatement ps = con.prepareStatement(builder.toString()); ps.setInt(1, gp); ps.setInt(2, logo); ps.setInt(3, logoColor); ps.setInt(4, logoBG); ps.setInt(5, logoBGColor); for (int i = 6; i < 11; i++) { ps.setString(i, rankTitles[i - 6]); } ps.setInt(11, capacity); ps.setString(12, notice); ps.setInt(13, this.id); ps.execute(); ps.close(); } else { PreparedStatement ps = con.prepareStatement("UPDATE characters SET guildid = 0, guildrank = 5 WHERE guildid = ?"); ps.setInt(1, this.id); ps.execute(); ps.close(); ps = con.prepareStatement("DELETE FROM guilds WHERE guildid = ?"); ps.setInt(1, this.id); ps.execute(); ps.close(); this.broadcast(MaplePacketCreator.guildDisband(this.id)); } } catch (SQLException se) { } } public int getId() { return id; } public int getLeaderId() { return leader; } public int getGP() { return gp; } public int getLogo() { return logo; } public void setLogo(int l) { logo = l; } public int getLogoColor() { return logoColor; } public void setLogoColor(int c) { logoColor = c; } public int getLogoBG() { return logoBG; } public void setLogoBG(int bg) { logoBG = bg; } public int getLogoBGColor() { return logoBGColor; } public void setLogoBGColor(int c) { logoBGColor = c; } public String getNotice() { if (notice == null) { return ""; } return notice; } public String getName() { return name; } public java.util.Collection<MapleGuildCharacter> getMembers() { return java.util.Collections.unmodifiableCollection(members); } public int getCapacity() { return capacity; } public int getSignature() { return signature; } public void broadcast(MaplePacket packet) { broadcast(packet, -1, BCOp.NONE); } public void broadcast(MaplePacket packet, int exception) { broadcast(packet, exception, BCOp.NONE); } public void broadcast(MaplePacket packet, int exceptionId, BCOp bcop) { synchronized (notifications) { if (bDirty) { buildNotifications(); } try { for (Byte b : Server.getInstance().getChannelServer(world)) { if (notifications.get(b).size() > 0) { if (bcop == BCOp.DISBAND) { Server.getInstance().getWorld(world).setGuildAndRank(notifications.get(b), 0, 5, exceptionId); } else if (bcop == BCOp.EMBELMCHANGE) { Server.getInstance().getWorld(world).changeEmblem(this.id, notifications.get(b), new MapleGuildSummary(this)); } else { Server.getInstance().getWorld(world).sendPacket(notifications.get(b), packet, exceptionId); } } } } catch (Exception re) { Output.print("Failed to contact channels for broadcast."); // fu? } } } public void guildMessage(MaplePacket serverNotice) { for (MapleGuildCharacter mgc : members) { for (Channel cs : Server.getInstance().getChannelsFromWorld(world)) { if (cs.getPlayerStorage().getCharacterById(mgc.getId()) != null) { cs.getPlayerStorage().getCharacterById(mgc.getId()).getClient().getSession().write(serverNotice); break; } } } } public final void setOnline(int cid, boolean online, byte channel) { boolean bBroadcast = true; for (MapleGuildCharacter mgc : members) { if (mgc.getId() == cid) { if (mgc.isOnline() && online) { bBroadcast = false; } mgc.setOnline(online); mgc.setChannel(channel); break; } } if (bBroadcast) { this.broadcast(MaplePacketCreator.guildMemberOnline(id, cid, online), cid); } bDirty = true; } public void guildChat(String name, int cid, String msg) { this.broadcast(MaplePacketCreator.multiChat(name, msg, 2), cid); } public String getRankTitle(int rank) { return rankTitles[rank - 1]; } public static int createGuild(int leaderId, String name) { try { Connection con = DatabaseConnection.getConnection(); PreparedStatement ps = con.prepareStatement("SELECT guildid FROM guilds WHERE name = ?"); ps.setString(1, name); ResultSet rs = ps.executeQuery(); if (rs.first()) { ps.close(); rs.close(); return 0; } ps.close(); rs.close(); ps = con.prepareStatement("INSERT INTO guilds (`leader`, `name`, `signature`) VALUES (?, ?, ?)"); ps.setInt(1, leaderId); ps.setString(2, name); ps.setInt(3, (int) System.currentTimeMillis()); ps.execute(); ps.close(); ps = con.prepareStatement("SELECT guildid FROM guilds WHERE leader = ?"); ps.setInt(1, leaderId); rs = ps.executeQuery(); rs.first(); int guildid = rs.getInt("guildid"); rs.close(); ps.close(); return guildid; } catch (Exception e) { return 0; } } public int addGuildMember(MapleGuildCharacter mgc) { synchronized (members) { if (members.size() >= capacity) { return 0; } for (int i = members.size() - 1; i >= 0; i--) { if (members.get(i).getGuildRank() < 5 || members.get(i).getName().compareTo(mgc.getName()) < 0) { members.add(i + 1, mgc); bDirty = true; break; } } } this.broadcast(MaplePacketCreator.newGuildMember(mgc)); return 1; } public void leaveGuild(MapleGuildCharacter mgc) { this.broadcast(MaplePacketCreator.memberLeft(mgc, false)); synchronized (members) { members.remove(mgc); bDirty = true; } } public void expelMember(MapleGuildCharacter initiator, String name, int cid) { synchronized (members) { java.util.Iterator<MapleGuildCharacter> itr = members.iterator(); MapleGuildCharacter mgc; while (itr.hasNext()) { mgc = itr.next(); if (mgc.getId() == cid && initiator.getGuildRank() < mgc.getGuildRank()) { this.broadcast(MaplePacketCreator.memberLeft(mgc, true)); itr.remove(); bDirty = true; try { if (mgc.isOnline()) { Server.getInstance().getWorld(mgc.getWorld()).setGuildAndRank(cid, 0, 5); } else { try { PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("INSERT INTO notes (`to`, `from`, `message`, `timestamp`) VALUES (?, ?, ?, ?)"); ps.setString(1, mgc.getName()); ps.setString(2, initiator.getName()); ps.setString(3, "You have been expelled from the guild."); ps.setLong(4, System.currentTimeMillis()); ps.executeUpdate(); ps.close(); } catch (SQLException e) { Output.print("Failed to expel a member from a guild.\n" + e); } Server.getInstance().getWorld(mgc.getWorld()).setOfflineGuildStatus((short) 0, (byte) 5, cid); } } catch (Exception re) { re.printStackTrace(); return; } return; } } Output.print("Unable to find guild member with name " + name + " and id " + cid); } } public void changeRank(int cid, int newRank) { for (MapleGuildCharacter mgc : members) { if (cid == mgc.getId()) { try { if (mgc.isOnline()) { Server.getInstance().getWorld(mgc.getWorld()).setGuildAndRank(cid, this.id, newRank); } else { Server.getInstance().getWorld(mgc.getWorld()).setOfflineGuildStatus((short) this.id, (byte) newRank, cid); } } catch (Exception re) { re.printStackTrace(); return; } mgc.setGuildRank(newRank); this.broadcast(MaplePacketCreator.changeRank(mgc)); return; } } } public void setGuildNotice(String notice) { this.notice = notice; writeToDB(false); this.broadcast(MaplePacketCreator.guildNotice(this.id, notice)); } public void memberLevelJobUpdate(MapleGuildCharacter mgc) { for (MapleGuildCharacter member : members) { if (mgc.equals(member)) { member.setJobId(mgc.getJobId()); member.setLevel(mgc.getLevel()); this.broadcast(MaplePacketCreator.guildMemberLevelJobUpdate(mgc)); break; } } } @Override public boolean equals(Object other) { if (!(other instanceof MapleGuildCharacter)) { return false; } MapleGuildCharacter o = (MapleGuildCharacter) other; return (o.getId() == id && o.getName().equals(name)); } @Override public int hashCode() { int hash = 3; hash = 89 * hash + (this.name != null ? this.name.hashCode() : 0); hash = 89 * hash + this.id; return hash; } public void changeRankTitle(String[] ranks) { for (int i = 0; i < 5; i++) { rankTitles[i] = ranks[i]; } this.broadcast(MaplePacketCreator.rankTitleChange(this.id, ranks)); this.writeToDB(false); } public void disbandGuild() { this.writeToDB(true); this.broadcast(null, -1, BCOp.DISBAND); } public void setGuildEmblem(short bg, byte bgcolor, short logo, byte logocolor) { this.logoBG = bg; this.logoBGColor = bgcolor; this.logo = logo; this.logoColor = logocolor; this.writeToDB(false); this.broadcast(null, -1, BCOp.EMBELMCHANGE); } public MapleGuildCharacter getMGC(int cid) { for (MapleGuildCharacter mgc : members) { if (mgc.getId() == cid) { return mgc; } } return null; } public boolean increaseCapacity() { if (capacity > 99) { return false; } capacity += 5; this.writeToDB(false); this.broadcast(MaplePacketCreator.guildCapacityChange(this.id, this.capacity)); return true; } public void gainGP(int amount) { this.gp += amount; this.writeToDB(false); this.guildMessage(MaplePacketCreator.updateGP(this.id, this.gp)); } public static MapleGuildResponse sendInvite(MapleClient c, String targetName) { MapleCharacter mc = c.getChannelServer().getPlayerStorage().getCharacterByName(targetName); if (mc == null) { return MapleGuildResponse.NOT_IN_CHANNEL; } if (mc.getGuildId() > 0) { return MapleGuildResponse.ALREADY_IN_GUILD; } mc.getClient().getSession().write(MaplePacketCreator.guildInvite(c.getPlayer().getGuildId(), c.getPlayer().getName())); return null; } public static void displayGuildRanks(MapleClient c, int npcid) { try { PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT `name`, `GP`, `logoBG`, `logoBGColor`, " + "`logo`, `logoColor` FROM guilds ORDER BY `GP` DESC LIMIT 50"); ResultSet rs = ps.executeQuery(); c.getSession().write(MaplePacketCreator.showGuildRanks(npcid, rs)); ps.close(); rs.close(); } catch (SQLException e) { Output.print("Failed to display guild ranks.\n" + e); } } public int getAllianceId() { return allianceId; } public void setAllianceId(int aid) { this.allianceId = aid; try { PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE guilds SET allianceId = ? WHERE guildid = ?"); ps.setInt(1, aid); ps.setInt(2, id); ps.executeUpdate(); ps.close(); } catch (SQLException e) { } } public int getIncreaseGuildCost(int size) { return 500000 * (size - 6) / 6; } }