package handling.channel; import client.MapleCharacter; import constants.ServerConstants; import constants.WorldConstants; import database.DatabaseConnection; import handling.MapleServerHandler; import handling.login.LoginServer; import handling.mina.MapleCodecFactory; import java.io.FileInputStream; import java.io.IOException; import java.net.InetSocketAddress; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.EnumMap; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.apache.log4j.Logger; import org.apache.mina.core.buffer.IoBuffer; import org.apache.mina.core.buffer.SimpleBufferAllocator; import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.executor.ExecutorFilter; import org.apache.mina.transport.socket.SocketSessionConfig; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; import scripting.event.EventScriptManager; import server.ManagerSin; import server.ServerProperties; import server.events.MapleCoconut; import server.events.MapleEvent; import server.events.MapleEventType; import server.events.MapleFitness; import server.events.MapleOla; import server.events.MapleOxQuiz; import server.events.MapleSnowball; import server.events.MapleSurvival; import server.life.PlayerNPC; import server.maps.AramiaFireWorks; import server.maps.MapleMap; import server.maps.MapleMapFactory; import server.maps.MapleMapObject; import server.shops.HiredMerchant; import server.shops.HiredMerchantSave; import server.squad.MapleSquad; import server.squad.MapleSquadType; import tools.ConcurrentEnumMap; import tools.FileoutputUtil; import tools.MaplePacketCreator; public class ChannelServer { public static long serverStartTime; private int cashRate; private int expRate; private int dropRate; private int traitRate; private int stateRate; private int statLimit; private int createGuildCost; private int globalRate; private int autoGain; private int autoNx; private int merchantTime; private short port; private static final short DEFAULT_PORT = 7575; private final int channel; private int running_MerchantID = 0; private int flags = 0; private int sharePrice = 0; private String ip; private String serverName; private boolean shutdown = false; private boolean finishedShutdown = false; private boolean MegaphoneMuteState = false; private boolean adminOnly = false; private boolean canPvp = false; private boolean shieldWardAll = false; private boolean autoPoints = false; private boolean checkSp = false; private boolean checkCash = false; private boolean useMapScript = false;//? private PlayerStorage players; private IoAcceptor acceptor; private final MapleMapFactory mapFactory; private EventScriptManager eventSM; private String eventSMs; private final AramiaFireWorks works = new AramiaFireWorks(); private static final Map<Integer, ChannelServer> instances = new HashMap(); private final Map<MapleSquadType, MapleSquad> mapleSquads = new ConcurrentEnumMap(MapleSquadType.class); private final Map<Integer, HiredMerchant> merchants = new HashMap(); private final List<PlayerNPC> playerNPCs = new LinkedList(); private ReentrantReadWriteLock merchLock = null; private ReentrantReadWriteLock.ReadLock mcReadLock = null; private ReentrantReadWriteLock.WriteLock mcWriteLock = null; private int eventmap = -1; private final Map<MapleEventType, MapleEvent> events = new EnumMap(MapleEventType.class); private String ShopPack; Connection shareCon; private static final Logger log = Logger.getLogger(ChannelServer.class); private final ManagerSin a = new ManagerSin(); private ChannelServer(int channel) { this.channel = channel; mapFactory = new MapleMapFactory(channel); merchLock = new ReentrantReadWriteLock(true); mcReadLock = merchLock.readLock(); mcWriteLock = merchLock.writeLock(); } public static Set<Integer> getAllInstance() { return new HashSet(instances.keySet()); } public void loadEvents() { if (!events.isEmpty()) { return; } events.put(MapleEventType.CokePlay, new MapleCoconut(channel, MapleEventType.CokePlay)); events.put(MapleEventType.Coconut, new MapleCoconut(channel, MapleEventType.Coconut)); events.put(MapleEventType.Fitness, new MapleFitness(channel, MapleEventType.Fitness)); events.put(MapleEventType.OlaOla, new MapleOla(channel, MapleEventType.OlaOla)); events.put(MapleEventType.OxQuiz, new MapleOxQuiz(channel, MapleEventType.OxQuiz)); events.put(MapleEventType.Snowball, new MapleSnowball(channel, MapleEventType.Snowball)); events.put(MapleEventType.Survival, new MapleSurvival(channel, MapleEventType.Survival)); } public void run_startup_configurations() { setChannel(channel); try { cashRate = ServerProperties.getProperty("CASH_RATE", 1); expRate = ServerProperties.getProperty("EXP_RATE",1); setExpRate(expRate); dropRate = ServerProperties.getProperty("DROP_RATE",1); setDropRate(dropRate); globalRate = ServerProperties.getProperty("GLOBAL_RATE", 1); traitRate = ServerProperties.getProperty("TRAIT_RATE", 1); stateRate = ServerProperties.getProperty("STATE_RATE", 4); statLimit = ServerProperties.getProperty("statLimit", 999); autoNx = Integer.parseInt(ServerProperties.getProperty("autoNx", "10")); autoGain = Integer.parseInt(ServerProperties.getProperty("autoGain", "10")); createGuildCost = ServerProperties.getProperty("createGuildCost", 10000000); merchantTime = ServerProperties.getProperty("merchantTime", 24); serverName = ServerProperties.getProperty("serverName", "MapleStory"); flags = ServerProperties.getProperty("flags", 0); adminOnly = ServerProperties.getProperty("admin", false); canPvp = ServerProperties.getProperty("canPvp", false); shieldWardAll = ServerProperties.getProperty("shieldWardAll", false); checkSp = ServerProperties.getProperty("checkSp", true); checkCash = ServerProperties.getProperty("checkCash", true); useMapScript = ServerProperties.getProperty("useMapScript", false); autoPoints = Boolean.parseBoolean(ServerProperties.getProperty("autoPoints", "false")); eventSMs = ServerProperties.getProperty("channel.events", ""); eventSM = new EventScriptManager(this, eventSMs.split(",")); port = Short.parseShort(ServerProperties.getProperty("channel.port" + channel, String.valueOf(DEFAULT_PORT + channel))); } catch (NumberFormatException e) { throw new RuntimeException(e); } ip = (ServerProperties.getProperty("channel.interface", ServerConstants.IP) + ":" + port); IoBuffer.setUseDirectBuffer(false); IoBuffer.setAllocator(new SimpleBufferAllocator()); acceptor = new NioSocketAcceptor(Runtime.getRuntime().availableProcessors() + 1); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 30); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter(new MapleCodecFactory())); //Executor threadPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() + 1); acceptor.getFilterChain().addLast("exceutor", new ExecutorFilter(/*threadPool*/)); //acceptor.getFilterChain().addLast("threadPool", new ExecutorFilter(threadPool)); players = new PlayerStorage(channel); loadEvents(); // 事件脚本 //loadShare(); try { acceptor.setHandler(new MapleServerHandler(channel)); acceptor.bind(new InetSocketAddress(port)); ((SocketSessionConfig) acceptor.getSessionConfig()).setTcpNoDelay(true); FileoutputUtil.log("频道" + channel + " 正在监听" + port + "端口"); eventSM.init(); } catch (IOException e) { FileoutputUtil.log("无法绑定" + port + "端口 (频道: " + getChannel() + ")" + e); } } public void shutdown() { if (finishedShutdown) { return; } broadcastPacket(MaplePacketCreator.serverMessageNotice(" 游戏即将关闭维护...")); shutdown = true; FileoutputUtil.log("频道 " + channel + " 正在清理活动脚本..."); eventSM.cancel(); FileoutputUtil.log("频道 " + channel + " 正在保存所有角色数据..."); getPlayerStorage().disconnectAll(); FileoutputUtil.log("频道 " + channel + " 解除绑定端口..."); acceptor.unbind(); acceptor = null; instances.remove(channel); setFinishShutdown(); } public void unbind() { acceptor.unbind(); } public boolean hasFinishedShutdown() { return finishedShutdown; } public MapleMapFactory getMapFactory() { return mapFactory; } public static ChannelServer newInstance(int channel) { return new ChannelServer(channel); } public static ChannelServer getInstance(int channel) { return (ChannelServer) instances.get(channel); } public void addPlayer(MapleCharacter chr) { getPlayerStorage().registerPlayer(chr); } public PlayerStorage getPlayerStorage() { if (players == null) { players = new PlayerStorage(channel); } return players; } public void removePlayer(MapleCharacter chr) { getPlayerStorage().deregisterPlayer(chr); } public void removePlayer(int idz, String namez) { getPlayerStorage().deregisterPlayer(idz, namez); } public void broadcastPacket(byte[] data) { getPlayerStorage().broadcastPacket(data); } public void broadcastSmegaPacket(byte[] data) { getPlayerStorage().broadcastSmegaPacket(data); } public void broadcastGMPacket(byte[] data) { getPlayerStorage().broadcastGMPacket(data); } public int getChannel() { return channel; } public void setChannel(int channel) { instances.put(channel, this); LoginServer.addChannel(channel); } public static ArrayList<ChannelServer> getAllInstances() { return new ArrayList(instances.values()); } public String getIP() { return ip; } public boolean isShutdown() { return shutdown; } public int getLoadedMaps() { return mapFactory.getLoadedMaps(); } public EventScriptManager getEventSM() { return eventSM; } public void reloadEvents() { eventSM.cancel(); eventSM = new EventScriptManager(this, ServerProperties.getProperty("channel.events", eventSMs).split(",")); eventSM.init(); } public int getExpRate(int world) { return WorldConstants.getById(world).getExp(); } public void setExpRate(int expRate) { WorldConstants.EXP_RATE = expRate; } public int getCashRate() { return cashRate; } public void setCashRate(int cashRate) { this.cashRate = cashRate; } public int getMesoRate(int world) { return WorldConstants.getById(world).getMeso(); } public void setMesoRate(int mesoRate) { WorldConstants.MESO_RATE = mesoRate; } public int getDropRate(int world) { return WorldConstants.getById(world).getDrop(); } public void setDropRate(int dropRate) { WorldConstants.DROP_RATE = dropRate; } public int getGlobalRate() { if (globalRate <= 0) { return 1; } return globalRate; } public void setGlobalRate(int rate) { globalRate = rate; } public int getStatLimit() { return statLimit; } public void setStatLimit(int limit) { statLimit = limit; } public static void startChannel_Main() { serverStartTime = System.currentTimeMillis(); for (int i = 1; i <= Math.min(20, Integer.parseInt(ServerProperties.getProperty("channel.count", "0"))); i++) { newInstance(i).run_startup_configurations(); } } public Map<MapleSquadType, MapleSquad> getAllSquads() { return Collections.unmodifiableMap(mapleSquads); } public MapleSquad getMapleSquad(String type) { return getMapleSquad(MapleSquadType.valueOf(type.toLowerCase())); } public MapleSquad getMapleSquad(MapleSquadType type) { return (MapleSquad) mapleSquads.get(type); } public boolean addMapleSquad(MapleSquad squad, String type) { MapleSquadType types = MapleSquadType.valueOf(type.toLowerCase()); if ((types != null) && (!mapleSquads.containsKey(types))) { mapleSquads.put(types, squad); squad.scheduleRemoval(); return true; } return false; } public boolean removeMapleSquad(MapleSquadType types) { if ((types != null) && (mapleSquads.containsKey(types))) { mapleSquads.remove(types); return true; } return false; } public int closeAllMerchant() { int ret = 0; mcWriteLock.lock(); try { Iterator merchants_ = merchants.entrySet().iterator(); while (merchants_.hasNext()) { HiredMerchant hm = (HiredMerchant) ((Map.Entry) merchants_.next()).getValue(); HiredMerchantSave.QueueShopForSave(hm); hm.getMap().removeMapObject(hm); merchants_.remove(); ret++; } } finally { mcWriteLock.unlock(); } for (int i = 910000001; i <= 910000022; i++) { for (MapleMapObject mmo : mapFactory.getMap(i).getAllHiredMerchantsThreadsafe()) { HiredMerchantSave.QueueShopForSave((HiredMerchant) mmo); ret++; } } return ret; } public void closeAllMerchants() { int ret = 0; long Start = System.currentTimeMillis(); mcWriteLock.lock(); try { Iterator hmit = merchants.entrySet().iterator(); while (hmit.hasNext()) { ((HiredMerchant) ((Map.Entry) hmit.next()).getValue()).closeShop(true, false); hmit.remove(); ret++; } } catch (Exception e) { log.error("关闭雇佣商店出现错误..." + e); } finally { mcWriteLock.unlock(); } FileoutputUtil.log("频道 " + channel + " 共保存雇佣商店: " + ret + " | 耗时: " + (System.currentTimeMillis() - Start) + " 毫秒."); } public int addMerchant(HiredMerchant hMerchant) { mcWriteLock.lock(); try { running_MerchantID += 1; merchants.put(running_MerchantID, hMerchant); int i = running_MerchantID; return i; } finally { mcWriteLock.unlock(); } } public void removeMerchant(HiredMerchant hMerchant) { mcWriteLock.lock(); try { merchants.remove(hMerchant.getStoreId()); } finally { mcWriteLock.unlock(); } } public boolean containsMerchant(int accId) { boolean contains = false; mcReadLock.lock(); try { for (HiredMerchant hm : merchants.values()) { if (hm.getOwnerAccId() == accId) { contains = true; break; } } } finally { mcReadLock.unlock(); } return contains; } public boolean containsMerchant(int accId, int chrId) { boolean contains = false; mcReadLock.lock(); try { for (HiredMerchant hm : merchants.values()) { if ((hm.getOwnerAccId() == accId) && (hm.getOwnerId() == chrId)) { contains = true; break; } } } finally { mcReadLock.unlock(); } return contains; } public List<HiredMerchant> searchMerchant(int itemSearch) { List list = new LinkedList(); mcReadLock.lock(); try { for (HiredMerchant hm : merchants.values()) { if (hm.searchItem(itemSearch).size() > 0) { list.add(hm); } } } finally { mcReadLock.unlock(); } return list; } public HiredMerchant getHiredMerchants(int accId, int chrId) { mcReadLock.lock(); try { for (HiredMerchant hm : merchants.values()) { if ((hm.getOwnerAccId() == accId) && (hm.getOwnerId() == chrId)) { HiredMerchant localHiredMerchant1 = hm; return localHiredMerchant1; } } } finally { mcReadLock.unlock(); } return null; } public void toggleMegaphoneMuteState() { MegaphoneMuteState = (!MegaphoneMuteState); } public boolean getMegaphoneMuteState() { return MegaphoneMuteState; } public int getEvent() { return eventmap; } public void setEvent(int ze) { eventmap = ze; } public MapleEvent getEvent(MapleEventType t) { return (MapleEvent) events.get(t); } public Collection<PlayerNPC> getAllPlayerNPC() { return playerNPCs; } public void addPlayerNPC(PlayerNPC npc) { if (playerNPCs.contains(npc)) { return; } playerNPCs.add(npc); getMapFactory().getMap(npc.getMapId()).addMapObject(npc); } public void removePlayerNPC(PlayerNPC npc) { if (playerNPCs.contains(npc)) { playerNPCs.remove(npc); getMapFactory().getMap(npc.getMapId()).removeMapObject(npc); } } public String getServerName() { return serverName; } public void setServerName(String sn) { serverName = sn; } public String getTrueServerName() { return serverName.substring(0, serverName.length() - 3); } public int getPort() { return port; } public static Set<Integer> getChannelServer() { return new HashSet(instances.keySet()); } public void setShutdown() { shutdown = true; FileoutputUtil.log("频道 " + channel + " 正在关闭和保存雇佣商店数据信息..."); } public void setFinishShutdown() { finishedShutdown = true; FileoutputUtil.log("频道 " + channel + " 已关闭完成."); } public boolean isAdminOnly() { return adminOnly; } public static int getChannelCount() { return instances.size(); } public int getTempFlag() { return flags; } public static Map<Integer, Integer> getChannelLoad() { Map ret = new HashMap(); for (ChannelServer cs : instances.values()) { ret.put(cs.getChannel(), cs.getConnectedClients()); } return ret; } public int getConnectedClients() { return getPlayerStorage().getConnectedClients(); } public void broadcastMessage(byte[] message) { broadcastPacket(message); } public void broadcastSmega(byte[] message) { broadcastSmegaPacket(message); } public void broadcastGMMessage(byte[] message) { broadcastGMPacket(message); } public void startMapEffect(String msg, int itemId) { startMapEffect(msg, itemId, 10); } public void startMapEffect(String msg, int itemId, int time) { for (MapleMap load : getMapFactory().getAllMaps()) { if (load.getCharactersSize() > 0) { load.startMapEffect(msg, itemId, time); } } } public AramiaFireWorks getFireWorks() { return works; } public int getTraitRate() { return traitRate; } public void saveAll() { int nos = 0; for (MapleCharacter chr : players.getAllCharacters()) { if (chr != null) { nos++; chr.saveToDB(false, false); } } FileoutputUtil.log("[自动保存] 已经将频道 " + channel + " 的 " + nos + " 个玩家的数据自动保存到数据中."); } public int getAutoGain() { return autoGain; } public void setAutoGain(int rate) { autoGain = rate; } public void AutoGain(int rate) { mapFactory.getMap(910000000).AutoGain(rate, 50); } public int getAutoNx() { return autoNx; } public void setAutoNx(int rate) { autoNx = rate; } public void AutoNx(int rate) { mapFactory.getMap(910000000).AutoNx(rate, isAutoPoints()); } public boolean isCanPvp() { return canPvp; } public boolean isShieldWardAll() { return shieldWardAll; } public void setShieldWardAll(boolean all) { shieldWardAll = all; } public int getStateRate() { return stateRate; } public void setStateRate(int stateRate) { this.stateRate = stateRate; } public int getCreateGuildCost() { return createGuildCost; } public static MapleCharacter getCharacterById(int id) { for (ChannelServer cserv_ : getAllInstances()) { MapleCharacter ret = cserv_.getPlayerStorage().getCharacterById(id); if (ret != null) { return ret; } } return null; } public static MapleCharacter getCharacterByName(String name) { for (ChannelServer cserv_ : getAllInstances()) { MapleCharacter ret = cserv_.getPlayerStorage().getCharacterByName(name); if (ret != null) { return ret; } } return null; } public int getSharePrice() { return sharePrice; } public void loadShare() { if ((channel != 1) || (finishedShutdown)) { return; } shareCon = DatabaseConnection.getConnection(); try { try (PreparedStatement ps = shareCon.prepareStatement("SELECT * FROM shares WHERE channelid = ?")) { ps.setInt(1, 1); try (ResultSet rs = ps.executeQuery()) { if (rs.next()) { sharePrice = rs.getInt("currentprice"); } else { throw new RuntimeException("[EXCEPTION] 无法加载股票数据."); } FileoutputUtil.log("目前的股票价格: " + sharePrice); } ps.close(); } } catch (SQLException e) { log.error("ERROR Load Shares", e); } } public void increaseShare(int share) { if ((channel != 1) || (finishedShutdown)) { return; } sharePrice += share; try { try (PreparedStatement ps = shareCon.prepareStatement("UPDATE shares SET currentprice = ? WHERE channelid = 1")) { ps.setInt(1, sharePrice); ps.executeUpdate(); } } catch (SQLException e) { log.error("ERROR Increase Shares", e); } } public void decreaseShare(int share) { if ((channel != 1) || (finishedShutdown)) { return; } sharePrice -= share; if (sharePrice < 0) { sharePrice = 0; } try { try (PreparedStatement ps = shareCon.prepareStatement("UPDATE shares SET currentprice = ? WHERE channelid = 1")) { ps.setInt(1, sharePrice); ps.executeUpdate(); } } catch (SQLException e) { log.error("ERROR Decrease Shares", e); } } public void saveShares() { if ((channel != 1) || (finishedShutdown)) { return; } try { try (PreparedStatement ps = shareCon.prepareStatement("UPDATE shares SET currentprice = ? WHERE channelid = 1")) { ps.setInt(1, sharePrice); ps.executeUpdate(); } } catch (SQLException e) { log.error("ERROR Save Shares", e); } } public int getMerchantTime() { return merchantTime; } public boolean isCheckSp() { return checkSp; } public boolean isCheckCash() { return checkCash; } public boolean isUseMapScript() { return useMapScript; } public boolean isAutoPoints() { return autoPoints; } }