Welcome!

Join our community of MMO enthusiasts and game developers! By registering, you'll gain access to discussions on the latest developments in MMO server files and collaborate with like-minded individuals. Join us today and unlock the potential of MMO server development!

Join Today!

Netty

Joined
Nov 9, 2012
Messages
608
Reaction score
164
Do not ask where to put this, or why you are getting errors when you put the code in. If you are asking that question then you should not use it at all. I have not tested shutting down the server, so let me know if it works fine. One thing I did notice was that server transitions do happen at a faster pace, let me know if you guys also witness the same thing.
Code:
/*
	This file is part of the OdinMS Maple Story Server
    Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
		       Matthias Butz <matze@odinms.de>
		       Jan Christian Meyer <vimes@odinms.de>

    This 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.netty;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
import io.netty.util.AttributeKey;

import java.util.List;

import tools.MapleAESOFB;
import client.MapleClient;

public class MaplePacketDecoder extends ByteToMessageDecoder {
	
      [USER=505313]Sup[/USER]pressWarnings("deprecation")
	private static final AttributeKey<DecoderState> DECODER_STATE_KEY = new AttributeKey<DecoderState>(MaplePacketDecoder.class.getName() + ".STATE");
   
    private static class DecoderState {
        public int packetlength = -1;
    }

	@Override
	protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> message) throws Exception {
        final MapleClient client = ctx.channel().attr(MapleClient.CLIENT_KEY).get();
        DecoderState decoderState = ctx.channel().attr(DECODER_STATE_KEY).get();
        if (decoderState == null) {
            decoderState = new DecoderState();
            ctx.channel().attr(DECODER_STATE_KEY).set(decoderState);
        }
        if (in.readableBytes() >= 4 && decoderState.packetlength == -1) {
            int packetHeader = in.readInt();
            if (!client.getReceiveCrypto().checkPacket(packetHeader)) {
            	ctx.channel().disconnect();
                return;
            }
            decoderState.packetlength = MapleAESOFB.getPacketLength(packetHeader);
        } else if (in.readableBytes() < 4 && decoderState.packetlength == -1) {
            return;
        }
        if (in.readableBytes() >= decoderState.packetlength) {
            byte decryptedPacket[] = new byte[decoderState.packetlength];
            in.readBytes(decryptedPacket);
            decoderState.packetlength = -1;
            client.getReceiveCrypto().crypt(decryptedPacket);
            MapleCustomEncryption.decryptData(decryptedPacket);
            message.add(decryptedPacket);
        }
	}
}
Code:
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>

This 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.netty;

import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;

import java.util.concurrent.locks.Lock;

import tools.MapleAESOFB;
import client.MapleClient;


public class MaplePacketEncoder extends MessageToByteEncoder<Object> {

	@Override
	protected void encode(ChannelHandlerContext ctx, Object message, ByteBuf buffer) throws Exception {	
        final MapleClient client = ctx.channel().attr(MapleClient.CLIENT_KEY).get();

        if (client != null) {
            final MapleAESOFB send_crypto = client.getSendCrypto();
            final byte[] input = (byte[]) message;
            final byte[] unencrypted = new byte[input.length];
            System.arraycopy(input, 0, unencrypted, 0, input.length);
            final byte[] ret = new byte[unencrypted.length + 4];
            final byte[] header = send_crypto.getPacketHeader(unencrypted.length);
            MapleCustomEncryption.encryptData(unencrypted);

            final Lock mutex = client.getLock();
            mutex.lock();
            try {
                send_crypto.crypt(unencrypted);
                System.arraycopy(header, 0, ret, 0, 4);
                System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
                buffer.writeBytes(ret);
            } finally {
                mutex.unlock();
            }
//            System.arraycopy(unencrypted, 0, ret, 4, unencrypted.length);
//            out.write(ByteBuffer.wrap(ret));
        } else {
        	buffer.writeBytes((byte[]) message);
        }
	}
}
Code:
package net.netty;

import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.socket.SocketChannel;
import net.MapleServerHandler;


public class ServerInitializer extends ChannelInitializer<SocketChannel> {
	
	private int world;
	private int channels;
	
	public ServerInitializer(int world, int channels) {
		this.world = world;
		this.channels = channels;
	}
	
	@Override
	protected void initChannel(SocketChannel channel) throws Exception {
		ChannelPipeline pipe = channel.pipeline();
		pipe.addLast("decoder", new MaplePacketDecoder()); // decodes the packet
		pipe.addLast("encoder", new MaplePacketEncoder()); //encodes the packet
		pipe.addLast("handler", new MapleServerHandler(world, channels));
	}

}
Code:
package net.netty;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import constants.ServerConstants;

public class ServerConnection {
	
	private int port;
	private int world = -1;
	private int channels = -1;
	private ServerBootstrap boot;
	private EventLoopGroup bossGroup = new NioEventLoopGroup(1); //The initial connection thread where all the new connections go to
	private EventLoopGroup workerGroup = new NioEventLoopGroup(); //Once the connection thread has finished it will be moved over to this group where the thread will be managed
	private Channel channel;
	
	public ServerConnection(int port) {
		this.port = port;
	}
	
	public ServerConnection(int port, int world, int channels) {
		this.port = port;
		this.world = world;
		this.channels = channels;
	} 
	
	public void run() {
		try {
			boot = new ServerBootstrap().group(bossGroup, workerGroup)
					.channel(NioServerSocketChannel.class)
					.option(ChannelOption.SO_BACKLOG, ServerConstants.MAXIMUM_CONNECTIONS)
					.childOption(ChannelOption.TCP_NODELAY, true)
					.childOption(ChannelOption.SO_KEEPALIVE, true)
					.childHandler(new ServerInitializer(world, channels));
			try {
				channel = boot.bind(ServerConstants.HOST, port).sync().channel().closeFuture().channel();
			}  catch(Exception e) {
				e.printStackTrace();
			} finally {
				System.out.println("Listening to port: " + port);
			}
		} catch(Exception e) {
			System.out.printf("Connection to %s failed.", channel.remoteAddress());
		}
	}
	
	public void close () {
		channel.close();
		bossGroup.shutdownGracefully();
		workerGroup.shutdownGracefully();
	}
}
Code:
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>

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

import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;

import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Random;

import net.server.Server;
import tools.FilePrinter;
import tools.MapleAESOFB;
import tools.MapleLogger;
import tools.MaplePacketCreator;
import tools.data.input.ByteArrayByteStream;
import tools.data.input.GenericSeekableLittleEndianAccessor;
import tools.data.input.SeekableLittleEndianAccessor;
import client.MapleClient;
import constants.ServerConstants;

public class MapleServerHandler extends ChannelInboundHandlerAdapter {

    private PacketProcessor processor;
    private int world = -1, channel = -1;
    private static final SimpleDateFormat sdf = new SimpleDateFormat("dd-MM-yyyy HH:mm");
    
    public MapleServerHandler() {
        this.processor = PacketProcessor.getProcessor(-1, -1);
    }

    public MapleServerHandler(int world, int channel) {
        this.processor = PacketProcessor.getProcessor(world, channel);
        this.world = world;
        this.channel = channel;
    }


	@Override
	public void channelActive(ChannelHandlerContext ctx) {
        if (!Server.getInstance().isOnline()) {
        	ctx.channel().close();
            return;
        }
        if (channel > -1 && world > -1) {
            if (Server.getInstance().getChannel(world, channel) == null) {
            	ctx.channel().close();
                return;
            }
        } else {
            FilePrinter.print(FilePrinter.SESSION, "IoSession with " + ctx.channel().remoteAddress() + " opened on " + sdf.format(Calendar.getInstance().getTime()), false);
        }

        byte key[] = {0x13, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00, (byte) 0xB4, 0x00, 0x00, 0x00, 0x1B, 0x00, 0x00, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00, 0x00, 0x52, 0x00, 0x00, 0x00};
        byte ivRecv[] = {70, 114, 122, 82};
        byte ivSend[] = {82, 48, 120, 115};
        ivRecv[3] = (byte) (Math.random() * 255);
        ivSend[3] = (byte) (Math.random() * 255);
        MapleAESOFB sendCypher = new MapleAESOFB(key, ivSend, (short) (0xFFFF - ServerConstants.VERSION));
        MapleAESOFB recvCypher = new MapleAESOFB(key, ivRecv, ServerConstants.VERSION);
        MapleClient client = new MapleClient(sendCypher, recvCypher, ctx.channel());
        client.setWorld(world);
        client.setChannel(channel);
        Random r = new Random();
        client.setSessionId(r.nextLong()); // Generates a random session id.
        ctx.channel().writeAndFlush(MaplePacketCreator.getHello(ServerConstants.VERSION, ivSend, ivRecv));
        ctx.channel().attr(MapleClient.CLIENT_KEY).set(client);
	}
	
	@Override
	public void channelInactive(ChannelHandlerContext ctx) {
        MapleClient client = (MapleClient) ctx.channel().attr(MapleClient.CLIENT_KEY).get();
        if (client != null) {
            try {
                boolean inCashShop = false;
                if (client.getPlayer() != null) {
                    inCashShop = client.getPlayer().getCashShop().isOpened();                  
                }
                client.disconnect(false, inCashShop);
            } catch (Throwable t) {
                FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, t);
            } finally {
            	ctx.channel().close();
                ctx.channel().attr(MapleClient.CLIENT_KEY).remove();      
                //client.empty();
            }
        }
	}
	
	@Override
	public void channelRead(ChannelHandlerContext ctx, Object message) { // handles the object which a current connection has sent to the server
        byte[] content = (byte[]) message;
        SeekableLittleEndianAccessor slea = new GenericSeekableLittleEndianAccessor(new ByteArrayByteStream(content));
        short packetId = slea.readShort();
        MapleClient client = ctx.channel().attr(MapleClient.CLIENT_KEY).get();
        final MaplePacketHandler packetHandler = processor.getHandler(packetId);
        if (packetHandler != null && packetHandler.validateState(client)) {
            try {
            	MapleLogger.logRecv(client, packetId, message);
                packetHandler.handlePacket(slea, client);
            } catch (final Throwable t) {
                FilePrinter.printError(FilePrinter.PACKET_HANDLER + packetHandler.getClass().getName() + ".txt", t, "Error for " + (client.getPlayer() == null ? "" : "player ; " + client.getPlayer() + " on map ; " + client.getPlayer().getMapId() + " - ") + "account ; " + client.getAccountName() + "\r\n" + slea.toString());
                //client.announce(MaplePacketCreator.enableActions());//bugs sometimes
            }
        }
	}
	
	@Override
	public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
		MapleClient client = (MapleClient) ctx.channel().attr(MapleClient.CLIENT_KEY).get();
	    if (client != null) {
	    	client.sendPing();
	    }
	}
	 
	@Override
	public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) {
    	if (cause instanceof IOException || cause instanceof ClassCastException) {
            return;
        }
        MapleClient mc = ctx.channel().attr(MapleClient.CLIENT_KEY).get();
        if (mc != null && mc.getPlayer() != null) {
            FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, cause, "Exception caught by: " + mc.getPlayer());
        }
	}
   
}
Code:
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>

This 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 client;

import io.netty.channel.Channel;
import io.netty.util.AttributeKey;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.net.InetAddress;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
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.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.script.ScriptEngine;

import net.server.Server;
import net.server.channel.ChannelServer;
import net.server.guild.MapleGuild;
import net.server.guild.MapleGuildCharacter;
import net.server.world.MapleMessengerCharacter;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import net.server.world.PartyOperation;
import net.server.world.World;
import scripting.npc.NPCConversationManager;
import scripting.npc.NPCScriptManager;
import scripting.quest.QuestActionManager;
import scripting.quest.QuestScriptManager;
import server.MapleMiniGame;
import server.MaplePlayerShop;
import server.MapleTrade;
import server.TimerManager;
import server.maps.FieldLimit;
import server.maps.HiredMerchant;
import server.maps.MapleMap;
import server.quest.MapleQuest;
import tools.DatabaseConnection;
import tools.FilePrinter;
import tools.HexTool;
import tools.MapleAESOFB;
import tools.MaplePacketCreator;
import client.inventory.MapleInventoryType;

public class MapleClient {

	public static final int LOGIN_NOTLOGGEDIN = 0;
	public static final int LOGIN_SERVER_TRANSITION = 1;
	public static final int LOGIN_LOGGEDIN = 2;
   [USER=505313]Sup[/USER]pressWarnings("deprecation")
	public static final AttributeKey<MapleClient> CLIENT_KEY = new AttributeKey<MapleClient>("Client");
	private MapleAESOFB send;
	private MapleAESOFB receive;
	private Channel connection;
	private MapleCharacter player;
	private int channel = 1;
	private int accId = 1;
	private boolean loggedIn = false;
	private boolean serverTransition = false;
	private Calendar birthday = null;
	private String accountName = null;
	private int world;
	private long lastPong;
	private int gmlevel;
	private Set<String> macs = new HashSet<>();
	private Map<String, ScriptEngine> engines = new HashMap<>();
	private ScheduledFuture<?> idleTask = null;
	private byte characterSlots = 3;
	private byte loginattempt = 0;
	private String pin = null;
	private int pinattempt = 0;
	private String pic = null;
	private int picattempt = 0;
	private byte gender = -1;
	private boolean disconnecting = false;
	private final Lock mutex = new ReentrantLock(true);
	private int votePoints;
	private int voteTime = -1;
	private long lastNpcClick;
	private long sessionId;

	public MapleClient(MapleAESOFB send, MapleAESOFB receive, Channel connection) {
		this.send = send;
		this.receive = receive;
		this.connection = connection;
	}

	public synchronized MapleAESOFB getReceiveCrypto() {
		return receive;
	}

	public synchronized MapleAESOFB getSendCrypto() {
		return send;
	}

	public synchronized Channel getSession() {
		return connection;
	}

	public MapleCharacter getPlayer() {
		return player;
	}

	public void setPlayer(MapleCharacter player) {
		this.player = player;
	}

	public void sendCharList(int server) {
		this.connection.writeAndFlush(MaplePacketCreator.getCharList(this, server));
	}

	public List<MapleCharacter> loadCharacters(int serverId) {
		List<MapleCharacter> chars = new ArrayList<>();
		try {
			for (CharNameAndId cni : loadCharactersInternal(serverId)) {
				chars.add(MapleCharacter.loadCharFromDB(cni.id, this, false));
			}
		} catch (Exception e) {
		}
		return chars;
	}

	public List<String> loadCharacterNames(int serverId) {
		List<String> chars = new ArrayList<>();
		for (CharNameAndId cni : loadCharactersInternal(serverId)) {
			chars.add(cni.name);
		}
		return chars;
	}

	private List<CharNameAndId> loadCharactersInternal(int serverId) {
		PreparedStatement ps;
		List<CharNameAndId> chars = new ArrayList<>();
		try {
			ps = DatabaseConnection.getConnection().prepareStatement("SELECT id, name FROM characters WHERE accountid = ? AND world = ?");
			ps.setInt(1, this.getAccID());
			ps.setInt(2, serverId);
			try (ResultSet rs = ps.executeQuery()) {
				while (rs.next()) {
					chars.add(new CharNameAndId(rs.getString("name"), rs.getInt("id")));
				}
			}
			ps.close();
		} catch (SQLException e) {
			e.printStackTrace();
		}
		return chars;
	}

	public boolean isLoggedIn() {
		return loggedIn;
	}

	public boolean hasBannedIP() {
		boolean ret = false;
		try {
			try (PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT COUNT(*) FROM ipbans WHERE ? LIKE CONCAT(ip, '%')")) {
				ps.setString(1, connection.remoteAddress().toString());
				try (ResultSet rs = ps.executeQuery()) {
					rs.next();
					if (rs.getInt(1) > 0) {
						ret = true;
					}
				}
			}
		} catch (SQLException e) {
		}
		return ret;
	}

	public int getVoteTime() {
		if (voteTime != -1) {
			return voteTime;
		}
		try {
			try (PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT date FROM bit_votingrecords WHERE UPPER(account) = UPPER(?)")) {
				ps.setString(1, accountName);
				try (ResultSet rs = ps.executeQuery()) {
					if (!rs.next()) {
						return -1;
					}
					voteTime = rs.getInt("date");
				}
			}
		} catch (SQLException e) {
			FilePrinter.printError("hasVotedAlready.txt", e);
			return -1;
		}
		return voteTime;
	}
        
        public void resetVoteTime() {
            voteTime = -1;
        }

	public boolean hasVotedAlready() {
		return false;
		/*
		Date currentDate = new Date();
		int timeNow = (int) (currentDate.getTime() / 1000);
		int difference = (timeNow - getVoteTime());
		return difference < 86400 && difference > 0;*/
	}
	
	public boolean hasBannedMac() {
		if (macs.isEmpty()) {
			return false;
		}
		boolean ret = false;
		int i;
		try {
			StringBuilder sql = new StringBuilder("SELECT COUNT(*) FROM macbans WHERE mac IN (");
			for (i = 0; i < macs.size(); i++) {
				sql.append("?");
				if (i != macs.size() - 1) {
					sql.append(", ");
				}
			}
			sql.append(")");
			try (PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement(sql.toString())) {
				i = 0;
				for (String mac : macs) {
					i++;
					ps.setString(i, mac);
				}
				try (ResultSet rs = ps.executeQuery()) {
					rs.next();
					if (rs.getInt(1) > 0) {
						ret = true;
					}
				}
			}
		} catch (Exception e) {
		}
		return ret;
	}
	

	// TODO: Recode to close statements...
	private void loadMacsIfNescessary() throws SQLException {
		if (macs.isEmpty()) {
			try (PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT macs FROM accounts WHERE id = ?")) {
				ps.setInt(1, accId);
				try (ResultSet rs = ps.executeQuery()) {
					if (rs.next()) {
						for (String mac : rs.getString("macs").split(", ")) {
							if (!mac.equals("")) {
								macs.add(mac);
							}
						}
					}
				}
			}
		}
	}
	
	public void banMacs() {
		Connection con = DatabaseConnection.getConnection();
		try {
			loadMacsIfNescessary();
			List<String> filtered = new LinkedList<>();
			try (PreparedStatement ps = con.prepareStatement("SELECT filter FROM macfilters"); ResultSet rs = ps.executeQuery()) {
				while (rs.next()) {
					filtered.add(rs.getString("filter"));
				}
			}
			try (PreparedStatement ps = con.prepareStatement("INSERT INTO macbans (mac) VALUES (?)")) {
				for (String mac : macs) {
					boolean matched = false;
					for (String filter : filtered) {
						if (mac.matches(filter)) {
							matched = true;
							break;
						}
					}
					if (!matched) {
						ps.setString(1, mac);
						ps.executeUpdate();
					}
				}
			}
		} catch (SQLException e) {
		}
	}

	public int finishLogin() {
		synchronized (MapleClient.class) {
			if (getLoginState() > LOGIN_NOTLOGGEDIN) { // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN
				loggedIn = false;
				return 7;
			}
			updateLoginState(LOGIN_LOGGEDIN);
		}
		return 0;
	}

	public void setPin(String pin) {
		this.pin = pin;
		try {
			try (PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET pin = ? WHERE id = ?")) {
				ps.setString(1, pin);
				ps.setInt(2, accId);
				ps.executeUpdate();
			}
		} catch (SQLException e) {
		}
	}

	public String getPin() {
		return pin;
	}

	public boolean checkPin(String other) {
		pinattempt++;
		if (pinattempt > 5) {
			getSession().close();
		}
		if (pin.equals(other)) {
			pinattempt = 0;
			return true;
		}
		return false;
	}

	public void setPic(String pic) {
		this.pic = pic;
		try {
			try (PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET pic = ? WHERE id = ?")) {
				ps.setString(1, pic);
				ps.setInt(2, accId);
				ps.executeUpdate();
			}
		} catch (SQLException e) {
		}
	}

	public String getPic() {
		return pic;
	}

	public boolean checkPic(String other) {
		picattempt++;
		if (picattempt > 5) {
			getSession().close();
		}
		if (pic.equals(other)) {
			picattempt = 0;
			return true;
		}
		return false;
	}

	public int login(String login, String pwd) {
		loginattempt++;
		if (loginattempt > 4) {
			getSession().close();
		}
		int loginok = 5;
		Connection con = DatabaseConnection.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = con.prepareStatement("SELECT id, password, salt, gender, banned, gm, pin, pic, characterslots, tos FROM accounts WHERE name = ?");
			ps.setString(1, login);
			rs = ps.executeQuery();
			if (rs.next()) {
				if (rs.getByte("banned") == 1) {
					return 3;
				}
				accId = rs.getInt("id");
				gmlevel = rs.getInt("gm");
				pin = rs.getString("pin");
				pic = rs.getString("pic");
				gender = rs.getByte("gender");
				characterSlots = rs.getByte("characterslots");
				String passhash = rs.getString("password");
				String salt = rs.getString("salt");
				//we do not unban
				byte tos = rs.getByte("tos");
				ps.close();
				rs.close();
				if (getLoginState() > LOGIN_NOTLOGGEDIN) { // already loggedin
					loggedIn = false;
					loginok = 7;
				} else if (pwd.equals(passhash) || checkHash(passhash, "SHA-1", pwd) || checkHash(passhash, "SHA-512", pwd + salt)) {
					if (tos == 0) {
						loginok = 23;
					} else {
						loginok = 0;
					}
				} else {
					loggedIn = false;
					loginok = 4;
				}
				ps = con.prepareStatement("INSERT INTO iplog (accountid, ip) VALUES (?, ?)");
				ps.setInt(1, accId);
				ps.setString(2, getSession().remoteAddress().toString());
				ps.executeUpdate();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (ps != null && !ps.isClosed()) {
					ps.close();
				}
				if (rs != null && !rs.isClosed()) {
					rs.close();
				}
			} catch (SQLException e) {
			}
		}
		if (loginok == 0) {
			loginattempt = 0;
		}
		return loginok;
	}

	public Calendar getTempBanCalendar() {
		Connection con = DatabaseConnection.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		final Calendar lTempban = Calendar.getInstance();
		try {
			ps = con.prepareStatement("SELECT `tempban` FROM accounts WHERE id = ?");
			ps.setInt(1, getAccID());
			rs = ps.executeQuery();
			if (!rs.next()) {
				return null;
			}
			long blubb = rs.getLong("tempban");
			if (blubb == 0) { // basically if timestamp in db is 0000-00-00
				return null;
			}
			lTempban.setTimeInMillis(rs.getTimestamp("tempban").getTime());
			return lTempban;
		} catch (SQLException e) {
		} finally {
			try {
				if (ps != null) {
					ps.close();
				}
				if (rs != null) {
					rs.close();
				}
			} catch (SQLException e) {
			}
		}
		return null;//why oh why!?!
	}

	public static long dottedQuadToLong(String dottedQuad) throws RuntimeException {
		String[] quads = dottedQuad.split("\\.");
		if (quads.length != 4) {
			throw new RuntimeException("Invalid IP Address format.");
		}
		long ipAddress = 0;
		for (int i = 0; i < 4; i++) {
			int quad = Integer.parseInt(quads[i]);
			ipAddress += quad % 256 * (long) Math.pow(256, 4 - i);
		}
		return ipAddress;
	}
	
	public void updateMacs(String macData) {
		macs.addAll(Arrays.asList(macData.split(", ")));
		StringBuilder newMacData = new StringBuilder();
		Iterator<String> iter = macs.iterator();
		PreparedStatement ps = null;
		while (iter.hasNext()) {
			String cur = iter.next();
			newMacData.append(cur);
			if (iter.hasNext()) {
				newMacData.append(", ");
			}
		}
		try {
			ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET macs = ? WHERE id = ?");
			ps.setString(1, newMacData.toString());
			ps.setInt(2, accId);
			ps.executeUpdate();
			ps.close();
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (ps != null && !ps.isClosed()) {
					ps.close();
				}
			} catch (SQLException ex) {
			}
		}
	}

	public void setAccID(int id) {
		this.accId = id;
	}

	public int getAccID() {
		return accId;
	}

	public void updateLoginState(int newstate) {
		try {
			Connection con = DatabaseConnection.getConnection();
			try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET loggedin = ?, lastlogin = CURRENT_TIMESTAMP() WHERE id = ?")) {
				ps.setInt(1, newstate);
				ps.setInt(2, getAccID());
				ps.executeUpdate();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
		if (newstate == LOGIN_NOTLOGGEDIN) {
			loggedIn = false;
			serverTransition = false;
		} else {
			serverTransition = (newstate == LOGIN_SERVER_TRANSITION);
			loggedIn = !serverTransition;
		}
	}

	public int getLoginState() {  // 0 = LOGIN_NOTLOGGEDIN, 1= LOGIN_SERVER_TRANSITION, 2 = LOGIN_LOGGEDIN
		try {
			Connection con = DatabaseConnection.getConnection();
			PreparedStatement ps = con.prepareStatement("SELECT loggedin, lastlogin, UNIX_TIMESTAMP(birthday) as birthday FROM accounts WHERE id = ?");
			ps.setInt(1, getAccID());
			ResultSet rs = ps.executeQuery();
			if (!rs.next()) {
				rs.close();
				ps.close();
				throw new RuntimeException("getLoginState - MapleClient");
			}
			birthday = Calendar.getInstance();
			long blubb = rs.getLong("birthday");
			if (blubb > 0) {
				birthday.setTimeInMillis(blubb * 1000);
			}
			int state = rs.getInt("loggedin");
			if (state == LOGIN_SERVER_TRANSITION) {
				if (rs.getTimestamp("lastlogin").getTime() + 30000 < System.currentTimeMillis()) {
					state = LOGIN_NOTLOGGEDIN;
					updateLoginState(LOGIN_NOTLOGGEDIN);
				}
			} else if (state == LOGIN_LOGGEDIN && player == null) {
				state = LOGIN_LOGGEDIN;
				updateLoginState(LOGIN_LOGGEDIN);
			}
			rs.close();
			ps.close();
			if (state == LOGIN_LOGGEDIN) {
				loggedIn = true;
			} else if (state == LOGIN_SERVER_TRANSITION) {
				ps = con.prepareStatement("UPDATE accounts SET loggedin = 0 WHERE id = ?");
				ps.setInt(1, getAccID());
				ps.executeUpdate();
				ps.close();
			} else {
				loggedIn = false;
			}
			return state;
		} catch (SQLException e) {
			loggedIn = false;
			e.printStackTrace();
			throw new RuntimeException("login state");
		}
	}

	public boolean checkBirthDate(Calendar date) {
		return date.get(Calendar.YEAR) == birthday.get(Calendar.YEAR) && date.get(Calendar.MONTH) == birthday.get(Calendar.MONTH) && date.get(Calendar.DAY_OF_MONTH) == birthday.get(Calendar.DAY_OF_MONTH);
	}

	private void removePlayer() {
		try {
			player.cancelAllBuffs(true);
			player.cancelAllDebuffs();
			final MaplePlayerShop mps = player.getPlayerShop();
			if (mps != null) {
				mps.removeVisitors();
				player.setPlayerShop(null);
			}
			final HiredMerchant merchant = player.getHiredMerchant();
			if (merchant != null) {
				if (merchant.isOwner(player)) {
					merchant.setOpen(true);
				} else {
					merchant.removeVisitor(player);
				}
				try {
					merchant.saveItems(false);
				} catch (SQLException ex) {
					System.out.println("Error while saving Hired Merchant items.");
				}
			}
			player.setMessenger(null);
			final MapleMiniGame game = player.getMiniGame();
			if (game != null) {
				player.setMiniGame(null);
				if (game.isOwner(player)) {
					player.getMap().broadcastMessage(MaplePacketCreator.removeCharBox(player));
					game.broadcastToVisitor(MaplePacketCreator.getMiniGameClose());
				} else {
					game.removeVisitor(player);
				}
			}
			NPCScriptManager.getInstance().dispose(this);
			QuestScriptManager.getInstance().dispose(this);
			if (player.getTrade() != null) {
				MapleTrade.cancelTrade(player);
			}
			if (player.getEventInstance() != null) {
				player.getEventInstance().playerDisconnected(player);
			}
			if (player.getMap() != null) {
				player.getMap().removePlayer(player);
			}

		} catch (final Throwable t) {
			FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, t);
		}
	}

	public final void disconnect(boolean shutdown, boolean cashshop) {//once per MapleClient instance
		if (disconnecting) {
			return;
		}
		disconnecting = true;
		if (player != null && player.isLoggedin() && player.getClient() != null) {
			MapleMap map = player.getMap();
			final MapleParty party = player.getParty();
			final int idz = player.getId();
			final int messengerid = player.getMessenger() == null ? 0 : player.getMessenger().getId();
			//final int fid = player.getFamilyId();
			final BuddyList bl = player.getBuddylist();
			final MaplePartyCharacter chrp = new MaplePartyCharacter(player);
			final MapleMessengerCharacter chrm = new MapleMessengerCharacter(player, 0);
			final MapleGuildCharacter chrg = player.getMGC();
			final MapleGuild guild = player.getGuild();

			removePlayer();
			player.saveCooldowns();
			player.saveToDB();
			if (channel == -1 || shutdown) {
				player = null;
				return;
			}
			final World worlda = getWorldServer();
			try {
				if (!cashshop) {
					if (!this.serverTransition) { // meaning not changing channels
						if (messengerid > 0) {
							worlda.leaveMessenger(messengerid, chrm);
						}
						/*    if (fid > 0) {
	                	   final MapleFamily family = worlda.getFamily(fid);
	                	   family.
	                   }*/                  
						for (MapleQuestStatus status : player.getStartedQuests()) { //This is for those quests that you have to stay logged in for a certain amount of time
							MapleQuest quest = status.getQuest();
							if (quest.getTimeLimit() > 0) {
								MapleQuestStatus newStatus = new MapleQuestStatus(quest, MapleQuestStatus.Status.NOT_STARTED);
								newStatus.setForfeited(player.getQuest(quest).getForfeited() + 1);
								player.updateQuest(newStatus);
							}
						}	                   
						if (guild != null) {
							final Server server = Server.getInstance();
							server.setGuildMemberOnline(chrg, false, player.getClient().getChannel());
							player.getClient().announce(MaplePacketCreator.showGuildInfo(player));
						}
						if (party != null) {
							chrp.setOnline(false);
							worlda.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp);
							if (map != null && party.getLeader().getId() == idz) {
								MaplePartyCharacter lchr = null;
								for (MaplePartyCharacter pchr : party.getMembers()) {
									if (pchr != null && map.getCharacterById(pchr.getId()) != null && (lchr == null || lchr.getLevel() <= pchr.getLevel())) {
										lchr = pchr;
									}
								}
								if (lchr != null) {
									worlda.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr);
								}
							}
						}                   
						if (bl != null) {
							worlda.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
						}
					}
				} else {
					if (!this.serverTransition) { // if dc inside of cash shop.
						if (party != null) {
							chrp.setOnline(false);
							worlda.updateParty(party.getId(), PartyOperation.LOG_ONOFF, chrp);
							if (map != null && party.getLeader().getId() == idz) {
								MaplePartyCharacter lchr = null;
								for (MaplePartyCharacter pchr : party.getMembers()) {
									if (pchr != null && map.getCharacterById(pchr.getId()) != null && (lchr == null || lchr.getLevel() <= pchr.getLevel())) {
										lchr = pchr;
									}
								}
								if (lchr != null) {
									worlda.updateParty(party.getId(), PartyOperation.CHANGE_LEADER, lchr);
								}
							}
						}	                	
						if (bl != null) {
							worlda.loggedOff(player.getName(), player.getId(), channel, player.getBuddylist().getBuddyIds());
						}
					}
				}
			} catch (final Exception e) {
				FilePrinter.printError(FilePrinter.ACCOUNT_STUCK, e);
			} finally {
				getChannelServer().removePlayer(player);
				if (!this.serverTransition) {
					worlda.removePlayer(player);
					if (player != null) {//no idea, occur :(
						player.empty(false);
					}
					player.logOff();
				}
				player = null;
			}
		}
		if (!serverTransition && isLoggedIn()) {
			updateLoginState(MapleClient.LOGIN_NOTLOGGEDIN);
			connection.attr(MapleClient.CLIENT_KEY).remove(); // prevents double dcing during login
			connection.close();
		}
		engines.clear();
	}

	private void clear() {
		this.accountName = null;
		this.macs = null;
		this.birthday = null;
		//this.engines = null;
		if (this.idleTask != null) {
			this.idleTask.cancel(true);
			this.idleTask = null;
		}
		this.player = null;
		this.receive = null;
		this.send = null;
		//this.session = null;
	}

	public int getChannel() {
		return channel;
	}

	public ChannelServer getChannelServer() {
		return Server.getInstance().getChannel(world, channel);
	}

	public World getWorldServer() {
		return Server.getInstance().getWorld(world);
	}

	public ChannelServer getChannelServer(byte channel) {
		return Server.getInstance().getChannel(world, channel);
	}

	public boolean deleteCharacter(int cid) {
		Connection con = DatabaseConnection.getConnection();
		
		MapleCharacter player = Server.getInstance().getWorld(0).getPlayerStorage().getCharacterById(cid);
		if (player != null){
			player.getClient().disconnect(false, false);
			disconnect(false, false);
			return false; //DC both and return, duck that
		}
		try {
			try (PreparedStatement ps = con.prepareStatement("SELECT id, guildid, guildrank, name, allianceRank FROM characters WHERE id = ? AND accountid = ?")) {
				ps.setInt(1, cid);
				ps.setInt(2, accId);
				try (ResultSet rs = ps.executeQuery()) {
					if (!rs.next()) {
						return false;
					}
					if (rs.getInt("guildid") > 0) {
						try {
							Server.getInstance().deleteGuildCharacter(new MapleGuildCharacter(cid, 0, rs.getString("name"), (byte) -1, (byte) -1, 0, rs.getInt("guildrank"), rs.getInt("guildid"), false, rs.getInt("allianceRank")));
						} catch (Exception re) {
							return false;
						}
					}
				}
			}
			
			try (PreparedStatement ps = con.prepareStatement("DELETE FROM wishlists WHERE charid = ?")) {
				ps.setInt(1, cid);
				ps.executeUpdate();
			}
			try (PreparedStatement ps = con.prepareStatement("DELETE FROM characters WHERE id = ?")) {
				ps.setInt(1, cid);
				ps.executeUpdate();
			}
			String[] toDel = {"famelog", "inventoryitems", "keymap", "queststatus", "savedlocations", "skillmacros", "skills", "eventstats"};
			for (String s : toDel) {
				MapleCharacter.deleteWhereCharacterId(con, "DELETE FROM `" + s + "` WHERE characterid = ?", cid);
			}
			return true;
		} catch (SQLException e) {
			e.printStackTrace();
			return false;
		}
	}

	public String getAccountName() {
		return accountName;
	}

	public void setAccountName(String a) {
		this.accountName = a;
	}

	public void setChannel(int channel) {
		this.channel = channel;
	}

	public int getWorld() {
		return world;
	}

	public void setWorld(int world) {
		this.world = world;
	}

	public void pongReceived() {
		lastPong = System.currentTimeMillis();
	}

	public void sendPing() {
		final long then = System.currentTimeMillis();
		announce(MaplePacketCreator.getPing());
		TimerManager.getInstance().schedule(new Runnable() {

			@Override
			public void run() {
				try {
					if (lastPong < then) {
						if (getSession() != null && getSession().isActive()) {
							getSession().close();
						}
					}
				} catch (NullPointerException e) {
				}
			}
		}, 15000);
	}

	public Set<String> getMacs() {
		return Collections.unmodifiableSet(macs);
	}

	public int getGMLevel() {
		return gmlevel;
	}

	public void setScriptEngine(String name, ScriptEngine e) {
		engines.put(name, e);
	}

	public ScriptEngine getScriptEngine(String name) {
		return engines.get(name);
	}

	public void removeScriptEngine(String name) {
		engines.remove(name);
	}

	public ScheduledFuture<?> getIdleTask() {
		return idleTask;
	}

	public void setIdleTask(ScheduledFuture<?> idleTask) {
		this.idleTask = idleTask;
	}

	public NPCConversationManager getCM() {
		return NPCScriptManager.getInstance().getCM(this);
	}

	public QuestActionManager getQM() {
		return QuestScriptManager.getInstance().getQM(this);
	}

	public boolean acceptToS() {
		boolean disconnectForBeingAFaggot = false;
		if (accountName == null) {
			return true;
		}
		try {
			PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT `tos` FROM accounts WHERE id = ?");
			ps.setInt(1, accId);
			ResultSet rs = ps.executeQuery();

			if (rs.next()) {
				if (rs.getByte("tos") == 1) {
					disconnectForBeingAFaggot = true;
				}
			}
			ps.close();
			rs.close();
			ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET tos = 1 WHERE id = ?");
			ps.setInt(1, accId);
			ps.executeUpdate();
			ps.close();
		} catch (SQLException e) {
		}
		return disconnectForBeingAFaggot;
	}

	public int getVotePoints(){
		int points = 0;
		try {
			PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("SELECT `votes` FROM accounts WHERE id = ?");
			ps.setInt(1, accId);
			ResultSet rs = ps.executeQuery();

			if (rs.next()) {
				points = rs.getInt("votes");
			}
			ps.close();
			rs.close();

		} catch (SQLException e) {
		}
		votePoints = points;
		return votePoints;
	}

	public void addVotePoints(int points) {
		votePoints += points;
		saveVotePoints();
	}

	public void useVotePoints(int points){
		if (points > votePoints){
			//Should not happen, should probably log this
			return;
		}
		votePoints -= points;
		saveVotePoints();
	}

	private void saveVotePoints() {
		try {
			Connection con = DatabaseConnection.getConnection();
			try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET votes = ? WHERE id = ?")) {
				ps.setInt(1, votePoints);
				ps.setInt(2, accId);
				ps.executeUpdate();
			}
		} catch (SQLException e) {
			e.printStackTrace();
		}
	}

	public final Lock getLock() {
		return mutex;
	}

	private static class CharNameAndId {

		public String name;
		public int id;

		public CharNameAndId(String name, int id) {
			super();
			this.name = name;
			this.id = id;
		}
	}

	private static boolean checkHash(String hash, String type, String password) {
		try {
			MessageDigest digester = MessageDigest.getInstance(type);
			digester.update(password.getBytes("UTF-8"), 0, password.length());
			return HexTool.toString(digester.digest()).replace(" ", "").toLowerCase().equals(hash);
		} catch (NoSuchAlgorithmException | UnsupportedEncodingException e) {
			throw new RuntimeException("Encoding the string failed", e);
		}
	}

	public short getCharacterSlots() {
		return characterSlots;
	}

	public boolean gainCharacterSlot() {
		if (characterSlots < 15) {
			Connection con = DatabaseConnection.getConnection();
			try {
				try (PreparedStatement ps = con.prepareStatement("UPDATE accounts SET characterslots = ? WHERE id = ?")) {
					ps.setInt(1, this.characterSlots += 1);
					ps.setInt(2, accId);
					ps.executeUpdate();
				}
			} catch (SQLException e) {
			}
			return true;
		}
		return false;
	}

	public final byte getGReason() {
		final Connection con = DatabaseConnection.getConnection();
		PreparedStatement ps = null;
		ResultSet rs = null;
		try {
			ps = con.prepareStatement("SELECT `greason` FROM `accounts` WHERE id = ?");
			ps.setInt(1, accId);
			rs = ps.executeQuery();
			if (rs.next()) {
				return rs.getByte("greason");
			}
		} catch (SQLException e) {
			e.printStackTrace();
		} finally {
			try {
				if (ps != null) {
					ps.close();
				}
				if (rs != null) {
					rs.close();
				}
			} catch (SQLException e) {
			}
		}
		return 0;
	}

	public byte getGender() {
		return gender;
	}

	public void setGender(byte m) {
		this.gender = m;
		try {
			try (PreparedStatement ps = DatabaseConnection.getConnection().prepareStatement("UPDATE accounts SET gender = ? WHERE id = ?")) {
				ps.setByte(1, gender);
				ps.setInt(2, accId);
				ps.executeUpdate();
			}
		} catch (SQLException e) {
		}
	}

	public synchronized void announce(final byte[] packet) {
		connection.writeAndFlush(packet);
	}


	public void changeChannel(int channel) {
		Server server = Server.getInstance();
		if (player.isBanned()) {
			disconnect(false, false);
			return;
		}
		if (!player.isAlive() || FieldLimit.CHANGECHANNEL.check(player.getMap().getFieldLimit())) {
			announce(MaplePacketCreator.enableActions());
			return;
		}
		String[] socket = Server.getInstance().getIP(getWorld(), channel).split(":");
		if (player.getTrade() != null) {
			MapleTrade.cancelTrade(getPlayer());
		}

		HiredMerchant merchant = player.getHiredMerchant();
		if (merchant != null) {
			if (merchant.isOwner(getPlayer())) {
				merchant.setOpen(true);
			} else {
				merchant.removeVisitor(getPlayer());
			}
		}
		server.getPlayerBuffStorage().addBuffsToStorage(player.getId(), player.getAllBuffs());
		player.cancelBuffEffects();
		player.cancelMagicDoor();
		//Canceling mounts? Noty
		if (player.getBuffedValue(MapleBuffStat.PUPPET) != null) {
			player.cancelEffectFromBuffStat(MapleBuffStat.PUPPET);
		}
		if (player.getBuffedValue(MapleBuffStat.COMBO) != null) {
			player.cancelEffectFromBuffStat(MapleBuffStat.COMBO);
		}
		player.getInventory(MapleInventoryType.EQUIPPED).checked(false); //test
		player.getMap().removePlayer(player);
		player.getClient().getChannelServer().removePlayer(player);
		player.getClient().updateLoginState(MapleClient.LOGIN_SERVER_TRANSITION);
		try {
			announce(MaplePacketCreator.getChannelChange(InetAddress.getByName(socket[0]), Integer.parseInt(socket[1])));
		} catch (IOException e) {
		}
	}

	public long getSessionId() {
		return this.sessionId;
	}

	public void setSessionId(long sessionId) {
		this.sessionId = sessionId;
	}  

	public boolean canClickNPC(){
		return lastNpcClick + 500 < System.currentTimeMillis();
	}

	public void setClickedNPC(){
		lastNpcClick = System.currentTimeMillis();
	}

	public void removeClickedNPC(){
		lastNpcClick = 0;
	}
}
Code:
/*
 This file is part of the OdinMS Maple Story Server
 Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
 Matthias Butz <matze@odinms.de>
 Jan Christian Meyer <vimes@odinms.de>

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

import java.io.FileInputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;

import net.netty.ServerConnection;
import net.server.channel.ChannelServer;
import net.server.guild.MapleAlliance;
import net.server.guild.MapleGuild;
import net.server.guild.MapleGuildCharacter;
import net.server.world.World;
import server.CashShop.CashItemFactory;
import server.MapleItemInformationProvider;
import server.TimerManager;
import server.quest.MapleQuest;
import tools.DatabaseConnection;
import tools.FilePrinter;
import tools.Pair;
import client.MapleCharacter;
import client.SkillFactory;
import constants.ServerConstants;

public class Server implements Runnable {
	
	
	private ServerConnection init;
    private List<Map<Integer, String>> channels = new LinkedList<>();
    private List<World> worlds = new ArrayList<>();
    private Properties subnetInfo = new Properties();
    private static Server instance = null;
    private List<Pair<Integer, String>> worldRecommendedList = new LinkedList<>();
    private Map<Integer, MapleGuild> guilds = new LinkedHashMap<>();
    private PlayerBuffStorage buffStorage = new PlayerBuffStorage();
    private Map<Integer, MapleAlliance> alliances = new LinkedHashMap<>();
    private boolean online = false;
    public static long uptime = System.currentTimeMillis();
    
    public static Server getInstance() {
        if (instance == null) {
            instance = new Server();
        }
        return instance;
    }

    public boolean isOnline() {
        return online;
    }

    public List<Pair<Integer, String>> worldRecommendedList() {
        return worldRecommendedList;
    }

    public void removeChannel(int worldid, int channel) {
        channels.remove(channel);

        World world = worlds.get(worldid);
        if (world != null) {
            world.removeChannel(channel);
        }
    }

    public ChannelServer getChannel(int world, int channel) {
        return worlds.get(world).getChannel(channel);
    }

    public List<ChannelServer> getChannelsFromWorld(int world) {
        return worlds.get(world).getChannels();
    }

    public List<ChannelServer> getAllChannels() {
        List<ChannelServer> channelz = new ArrayList<>();
        for (World world : worlds) {
            for (ChannelServer ch : world.getChannels()) {
                channelz.add(ch);
            }
        }
        return channelz;
    }

    public String getIP(int world, int channel) {
        return channels.get(world).get(channel);
    }

    @Override
    public void run() {
        Properties p = new Properties();
        try {
            p.load(new FileInputStream("world.ini"));
        } catch (Exception e) {
            System.out.println("Please start create_server.bat");
            System.exit(0);
        }
        System.out.println(ServerConstants.SERVER_NAME + " v" + ServerConstants.VERSION + " starting up.\r\n");
        if (ServerConstants.SHUTDOWNHOOK) {
        	Runtime.getRuntime().addShutdownHook(new Thread(shutdown(false)));
        }
        DatabaseConnection.getConnection();
        Connection c = DatabaseConnection.getConnection();
        try {
            PreparedStatement ps = c.prepareStatement("UPDATE accounts SET loggedin = 0");
            ps.executeUpdate();
            ps.close();
            ps = c.prepareStatement("UPDATE characters SET HasMerchant = 0");
            ps.executeUpdate();
            ps.close();
        } catch (SQLException sqle) {
        }
        init = new ServerConnection(ServerConstants.SERVER_PORT);
        init.run();
        TimerManager tMan = TimerManager.getInstance();
        tMan.start();
        tMan.register(tMan.purge(), 300000);//Purging ftw...
        tMan.register(new RankingWorker(), ServerConstants.RANKING_INTERVAL);
        long timeToTake = System.currentTimeMillis();
        SkillFactory.loadAllSkills();
        System.out.println("Skills loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds");

        timeToTake = System.currentTimeMillis();
        MapleItemInformationProvider.getInstance().getAllItems();

        CashItemFactory.getSpecialCashItems();
        System.out.println("Items loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n");
        
		timeToTake = System.currentTimeMillis();
		// Works, but takes 2-3 minutes to load.
		MapleQuest.loadAllQuest();
		System.out.println("Quest loaded in " + ((System.currentTimeMillis() - timeToTake) / 1000.0) + " seconds\r\n");
		
		
        try {
            for (int i = 0; i < Integer.parseInt(p.getProperty("worlds")); i++) {
                System.out.println("Starting world " + i);
                World world = new World(i,
                        Integer.parseInt(p.getProperty("flag" + i)),
                        p.getProperty("eventmessage" + i),
                        ServerConstants.EXP_RATE,
                        ServerConstants.DROP_RATE,
                        ServerConstants.MESO_RATE,
                        ServerConstants.BOSS_DROP_RATE);

                worldRecommendedList.add(new Pair<>(i, p.getProperty("whyamirecommended" + i)));
                worlds.add(world);
                channels.add(new LinkedHashMap<Integer, String>());
                for (int j = 0; j < Integer.parseInt(p.getProperty("channels" + i)); j++) {
                    int channelid = j + 1;
                    ChannelServer channel = new ChannelServer(i, channelid);
                    world.addChannel(channel);
                    channels.get(i).put(channelid, channel.getIP());
                }
                world.setServerMessage(p.getProperty("servermessage" + i));
                System.out.println("Finished loading world " + i + "\r\n");
            }
        } catch (Exception e) {
            System.out.println("Error in moople.ini, start CreateINI.bat to re-make the file.");
            e.printStackTrace();//For those who get errors
            System.exit(0);
        }
        System.out.println(ServerConstants.SERVER_NAME + " is now online.");
        online = true;
    }

    public void shutdown() {
    	try {
	        TimerManager.getInstance().stop();
	        init.close();
    	} catch (NullPointerException e) {
    		FilePrinter.printError(FilePrinter.EXCEPTION_CAUGHT, e);
    	}
        System.out.println("Server offline.");
        System.exit(0);// BOEIEND :D
    }

    public static void main(String args[]) {
        Server.getInstance().run();
    }

    public Properties getSubnetInfo() {
        return subnetInfo;
    }

    public MapleAlliance getAlliance(int id) {
        synchronized (alliances) {
            if (alliances.containsKey(id)) {
                return alliances.get(id);
            }
            return null;
        }
    }

    public void addAlliance(int id, MapleAlliance alliance) {
        synchronized (alliances) {
            if (!alliances.containsKey(id)) {
                alliances.put(id, alliance);
            }
        }
    }

    public void disbandAlliance(int id) {
        synchronized (alliances) {
            MapleAlliance alliance = alliances.get(id);
            if (alliance != null) {
                for (Integer gid : alliance.getGuilds()) {
                    guilds.get(gid).setAllianceId(0);
                }
                alliances.remove(id);
            }
        }
    }

    public void allianceMessage(int id, final byte[] packet, int exception, int guildex) {
        MapleAlliance alliance = alliances.get(id);
        if (alliance != null) {
            for (Integer gid : alliance.getGuilds()) {
                if (guildex == gid) {
                    continue;
                }
                MapleGuild guild = guilds.get(gid);
                if (guild != null) {
                    guild.broadcast(packet, exception);
                }
            }
        }
    }

    public boolean addGuildtoAlliance(int aId, int guildId) {
        MapleAlliance alliance = alliances.get(aId);
        if (alliance != null) {
            alliance.addGuild(guildId);
            return true;
        }
        return false;
    }

    public boolean removeGuildFromAlliance(int aId, int guildId) {
        MapleAlliance alliance = alliances.get(aId);
        if (alliance != null) {
            alliance.removeGuild(guildId);
            return true;
        }
        return false;
    }

    public boolean setAllianceRanks(int aId, String[] ranks) {
        MapleAlliance alliance = alliances.get(aId);
        if (alliance != null) {
            alliance.setRankTitle(ranks);
            return true;
        }
        return false;
    }

    public boolean setAllianceNotice(int aId, String notice) {
        MapleAlliance alliance = alliances.get(aId);
        if (alliance != null) {
            alliance.setNotice(notice);
            return true;
        }
        return false;
    }

    public boolean increaseAllianceCapacity(int aId, int inc) {
        MapleAlliance alliance = alliances.get(aId);
        if (alliance != null) {
            alliance.increaseCapacity(inc);
            return true;
        }
        return false;
    }

    public Set<Integer> getChannelServer(int world) {
        return new HashSet<>(channels.get(world).keySet());
    }

    public byte getHighestChannelId() {
        byte highest = 0;
        for (Iterator<Integer> it = channels.get(0).keySet().iterator(); it.hasNext();) {
            Integer channel = it.next();
            if (channel != null && channel.intValue() > highest) {
                highest = channel.byteValue();
            }
        }
        return highest;
    }

    public int createGuild(int leaderId, String name) {
        return MapleGuild.createGuild(leaderId, name);
    }

    public MapleGuild getGuild(int id, int world, MapleGuildCharacter mgc) {
        synchronized (guilds) {
            if (guilds.get(id) != null) {
                return guilds.get(id);
            }
            MapleGuild g = new MapleGuild(id, world);
            if (g.getId() == -1) {
                return null;
            }
            if (mgc != null) {
                g.setOnline(mgc.getId(), true, mgc.getChannel());
            }
            guilds.put(id, g);
            return g;
        }
    }

    public void clearGuilds() {//remake
        synchronized (guilds) {
            guilds.clear();
        }
        //for (List<Channel> world : worlds.values()) {
        //reloadGuildCharacters();
    }

    public void setGuildMemberOnline(MapleGuildCharacter mgc, boolean bOnline, int channel) {
        MapleGuild g = getGuild(mgc.getGuildId(), mgc.getWorld(), mgc);
        g.setOnline(mgc.getId(), bOnline, channel);
    }

    public int addGuildMember(MapleGuildCharacter mgc) {
        MapleGuild g = guilds.get(mgc.getGuildId());
        if (g != null) {
            return g.addGuildMember(mgc);
        }
        return 0;
    }

    public boolean setGuildAllianceId(int gId, int aId) {
        MapleGuild guild = guilds.get(gId);
        if (guild != null) {
            guild.setAllianceId(aId);
            return true;
        }
        return false;
    }

    public void leaveGuild(MapleGuildCharacter mgc) {
        MapleGuild g = guilds.get(mgc.getGuildId());
        if (g != null) {
            g.leaveGuild(mgc);
        }
    }

    public void guildChat(int gid, String name, int cid, String msg) {
        MapleGuild g = guilds.get(gid);
        if (g != null) {
            g.guildChat(name, cid, msg);
        }
    }

    public void changeRank(int gid, int cid, int newRank) {
        MapleGuild g = guilds.get(gid);
        if (g != null) {
            g.changeRank(cid, newRank);
        }
    }

    public void expelMember(MapleGuildCharacter initiator, String name, int cid) {
        MapleGuild g = guilds.get(initiator.getGuildId());
        if (g != null) {
            g.expelMember(initiator, name, cid);
        }
    }

    public void setGuildNotice(int gid, String notice) {
        MapleGuild g = guilds.get(gid);
        if (g != null) {
            g.setGuildNotice(notice);
        }
    }

    public void memberLevelJobUpdate(MapleGuildCharacter mgc) {
        MapleGuild g = guilds.get(mgc.getGuildId());
        if (g != null) {
            g.memberLevelJobUpdate(mgc);
        }
    }

    public void changeRankTitle(int gid, String[] ranks) {
        MapleGuild g = guilds.get(gid);
        if (g != null) {
            g.changeRankTitle(ranks);
        }
    }

    public void setGuildEmblem(int gid, short bg, byte bgcolor, short logo, byte logocolor) {
        MapleGuild g = guilds.get(gid);
        if (g != null) {
            g.setGuildEmblem(bg, bgcolor, logo, logocolor);
        }
    }

    public void disbandGuild(int gid) {
        synchronized (guilds) {
            MapleGuild g = guilds.get(gid);
            g.disbandGuild();
            guilds.remove(gid);
        }
    }

    public boolean increaseGuildCapacity(int gid) {
        MapleGuild g = guilds.get(gid);
        if (g != null) {
            return g.increaseCapacity();
        }
        return false;
    }

    public void gainGP(int gid, int amount) {
        MapleGuild g = guilds.get(gid);
        if (g != null) {
            g.gainGP(amount);
        }
    }
	
	public void guildMessage(int gid, byte[] packet) {
		guildMessage(gid, packet, -1);
	}
	
	public void guildMessage(int gid, byte[] packet, int exception) {
		MapleGuild g = guilds.get(gid);
		if(g != null) {
			g.broadcast(packet, exception);
		}
	}

    public PlayerBuffStorage getPlayerBuffStorage() {
        return buffStorage;
    }

    public void deleteGuildCharacter(MapleGuildCharacter mgc) {
        setGuildMemberOnline(mgc, false, (byte) -1);
        if (mgc.getGuildRank() > 1) {
            leaveGuild(mgc);
        } else {
            disbandGuild(mgc.getGuildId());
        }
    }

    public void reloadGuildCharacters(int world) {
        World worlda = getWorld(world);
        for (MapleCharacter mc : worlda.getPlayerStorage().getAllCharacters()) {
            if (mc.getGuildId() > 0) {
                setGuildMemberOnline(mc.getMGC(), true, worlda.getId());
                memberLevelJobUpdate(mc.getMGC());
            }
        }
        worlda.reloadGuildSummary();
    }

    public void broadcastMessage(final byte[] packet) {
        for (ChannelServer ch : getChannelsFromWorld(0)) {
            ch.broadcastPacket(packet);
        }
    }

    public void broadcastGMMessage(final byte[] packet) {
        for (ChannelServer ch : getChannelsFromWorld(0)) {
            ch.broadcastGMPacket(packet);
        }
    }
    
    public boolean isGmOnline() {
        for (ChannelServer ch : getChannelsFromWorld(0)) {
        	for (MapleCharacter player : ch.getPlayerStorage().getAllCharacters()) {
        		if (player.isGM()){
        			return true;
        		}
        	}
        }
        return false;
    }
    
    public World getWorld(int id) {
        return worlds.get(id);
    }

    public List<World> getWorlds() {
        return worlds;
    }

    public final Runnable shutdown(final boolean restart) {//only once :D
        return new Runnable() {
            @Override
            public void run() {
                System.out.println((restart ? "Restarting" : "Shutting down") + " the server!\r\n");
                if (getWorlds() == null) return;//already shutdown
                for (World w : getWorlds()) {
                    w.shutdown();
                }
                /*for (World w : getWorlds()) {
                    while (w.getPlayerStorage().getAllCharacters().size() > 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ie) {
                            System.err.println("duck MY LIFE");
                        }
                    }
                }
                for (Channel ch : getAllChannels()) {
                    while (ch.getConnectedClients() > 0) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ie) {
                            System.err.println("duck MY LIFE");
                        }
                    }
                }*/

                TimerManager.getInstance().purge();
                TimerManager.getInstance().stop();

                for (ChannelServer ch : getAllChannels()) {
                    while (!ch.finishedShutdown()) {
                        try {
                            Thread.sleep(1000);
                        } catch (InterruptedException ie) {
                            System.err.println("duck MY LIFE");
                        }
                    }
                }
                worlds.clear();
                worlds = null;
                channels.clear();
                channels = null;
                worldRecommendedList.clear();
                worldRecommendedList = null;

                System.out.println("Worlds + Channels are offline.");
                init.close();
                init = null;
                if (!restart) {
                    System.exit(0);
                } else {
                    System.out.println("\r\nRestarting the server....\r\n");
                    try {
                        instance.finalize();//FUU I CAN AND IT'S FREE
                    } catch (Throwable ex) {
                    }
                    instance = null;
                    System.gc();
                    getInstance().run();//DID I DO EVERYTHING?! D:
                }
            }
        };
    }
}
Code:
/*
This file is part of the OdinMS Maple Story Server
Copyright (C) 2008 Patrick Huy <patrick.huy@frz.cc>
Matthias Butz <matze@odinms.de>
Jan Christian Meyer <vimes@odinms.de>

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

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.locks.ReentrantReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock.WriteLock;

import net.netty.ServerConnection;
import net.server.PlayerStorage;
import net.server.world.MapleParty;
import net.server.world.MaplePartyCharacter;
import provider.MapleDataProviderFactory;
import scripting.event.EventScriptManager;
import server.TimerManager;
import server.events.gm.MapleEvent;
import server.expeditions.MapleExpedition;
import server.expeditions.MapleExpeditionType;
import server.maps.HiredMerchant;
import server.maps.MapleMap;
import server.maps.MapleMapFactory;
import tools.MaplePacketCreator;
import client.MapleCharacter;
import constants.ServerConstants;

public final class ChannelServer {
	
	
	private ServerConnection init;
    private int port = 7575;
    private PlayerStorage players = new PlayerStorage();
    private int world, channel;
    private String ip, serverMessage;
    private MapleMapFactory mapFactory;
    private EventScriptManager eventSM;
    private Map<Integer, HiredMerchant> hiredMerchants = new HashMap<>();
    private final Map<Integer, Integer> storedVars = new HashMap<>();
	private ReentrantReadWriteLock merchant_lock = new ReentrantReadWriteLock(true);
    private List<MapleExpedition> expeditions = new ArrayList<>();
    private List<MapleExpeditionType> expedType = new ArrayList<>();
    private MapleEvent event;
    private boolean finishedShutdown = false;
    
    public ChannelServer(final int world, final int channel) {
        this.world = world;
        this.channel = channel;
        this.mapFactory = new MapleMapFactory(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());
            port = 7575 + this.channel - 1;
            port += (world * 100);
            ip = ServerConstants.HOST + ":" + port;
            init = new ServerConnection(port, world, channel);
            init.run();
            TimerManager.getInstance().register(new respawnMaps(), 10000);
            for (MapleExpeditionType exped : MapleExpeditionType.values()) {
            	expedType.add(exped);
            }
            eventSM.init();
            
            System.out.println("\tChannel " + getId() + ": Listening on port " + port);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public void reloadEventScriptManager() {
    	eventSM.cancel();
    	eventSM = null;
    	eventSM = new EventScriptManager(this, getEvents());
    	eventSM.init();
    }

    public final void shutdown() {
        try {
            System.out.println("Shutting down Channel " + channel + " on World " + world);
            
            closeAllMerchants();
            players.disconnectAll();
            init.close();
            
            finishedShutdown = true;
            System.out.println("Successfully shut down Channel " + channel + " on World " + world + "\r\n");          
        } catch (Exception e) {
            System.err.println("Error while shutting down Channel " + channel + " on World " + world + "\r\n" + e);
        }
    }

    public void closeAllMerchants() {
        WriteLock wlock = merchant_lock.writeLock();
        wlock.lock();
        try {
            final Iterator<HiredMerchant> hmit = hiredMerchants.values().iterator();
            while (hmit.hasNext()) {
                hmit.next().forceClose();
                hmit.remove();
            }
        } catch (Exception e) {
			e.printStackTrace();
        } finally {
            wlock.unlock();
        }
    }
    
    public MapleMapFactory getMapFactory() {
        return mapFactory;
    }

    public int getWorld() {
        return world;
    }

    public void addPlayer(MapleCharacter chr) {
        players.addPlayer(chr);
        chr.announce(MaplePacketCreator.serverMessage(serverMessage));
    }

    public PlayerStorage getPlayerStorage() {
        return players;
    }

    public void removePlayer(MapleCharacter chr) {
        players.removePlayer(chr.getId());
    }

    public int getConnectedClients() {
        return players.getAllCharacters().size();
    }

    public void broadcastPacket(final byte[] data) {
        for (MapleCharacter chr : players.getAllCharacters()) {
            chr.announce(data);
        }
    }
    
    public final int getId() {
        return channel;
    }

    public String getIP() {
        return ip;
    }

    public MapleEvent getEvent() {
        return event;
    }

    public void setEvent(MapleEvent event) {
        this.event = event;
    }

    public EventScriptManager getEventSM() {
        return eventSM;
    }

    public void broadcastGMPacket(final byte[] data) {
        for (MapleCharacter chr : players.getAllCharacters()) {
            if (chr.isGM()) {
                chr.announce(data);
            }
        }
    }

    public List<MapleCharacter> getPartyMembers(MapleParty party) {
        List<MapleCharacter> partym = new ArrayList<>(8);
        for (MaplePartyCharacter partychar : party.getMembers()) {
            if (partychar.getChannel() == getId()) {
                MapleCharacter chr = getPlayerStorage().getCharacterByName(partychar.getName());
                if (chr != null) {
                    partym.add(chr);
                }
            }
        }
        return partym;
    }
        
    public class respawnMaps implements Runnable {

        @Override
        public void run() {
            for (Entry<Integer, MapleMap> map : mapFactory.getMaps().entrySet()) {
                map.getValue().respawn();
            }
        }
    }

    public Map<Integer, HiredMerchant> getHiredMerchants() {
        return hiredMerchants;
    }

    public void addHiredMerchant(int chrid, HiredMerchant hm) {
        WriteLock wlock = merchant_lock.writeLock();
        wlock.lock();
        try {
            hiredMerchants.put(chrid, hm);
        } finally {
            wlock.unlock();
        }
    }

    public void removeHiredMerchant(int chrid) {
        WriteLock wlock = merchant_lock.writeLock();
        wlock.lock();
        try {        
            hiredMerchants.remove(chrid);
        } finally {
            wlock.unlock();
        }
        }

    public int[] multiBuddyFind(int charIdFrom, int[] characterIds) {
        List<Integer> ret = new ArrayList<>(characterIds.length);
        PlayerStorage playerStorage = getPlayerStorage();
        for (int characterId : characterIds) {
            MapleCharacter chr = playerStorage.getCharacterById(characterId);
            if (chr != null) {
                if (chr.getBuddylist().containsVisible(charIdFrom)) {
                    ret.add(characterId);
                }
            }
        }
        int[] retArr = new int[ret.size()];
        int pos = 0;
        for (Integer i : ret) {
            retArr[pos++] = i.intValue();
        }
        return retArr;
    }
    
    public List<MapleExpedition> getExpeditions() {
    	return expeditions;
    }
    
    public boolean isConnected(String name) {
        return getPlayerStorage().getCharacterByName(name) != null;
    }
    
    public boolean finishedShutdown() {
        return finishedShutdown;
    }
    
    public void setServerMessage(String message) {
        this.serverMessage = message;
        broadcastPacket(MaplePacketCreator.serverMessage(message));
    }
    
    private static String [] getEvents(){
    	List<String> events = new ArrayList<String>();
    	for (File file : new File("scripts/event").listFiles()){
    		events.add(file.getName().substring(0, file.getName().length() - 3));
    	}
    	return events.toArray(new String[0]);
    }
	
	public int getStoredVar(int key) {
		if(storedVars.containsKey(key))
            return storedVars.get(key);
        return 0;
    }
    
    public void setStoredVar(int key, int val) {
        this.storedVars.put(key, val);
    }
}
 
Last edited:
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
This just looks like leaftown, the same thing Fraysa did. How does copying over from another src and releasing help? On top of that, you still use MapleServerHandler (and you limit this post to the 'PacketProcessor'). You even pasted entire files instead of the netty specific code? wtf is this, how is it even a release?

Do you know the benefits or cons of switching from Mina to Netty? You may want to include those in your awful release so people know what they are getting from this.
 
Joined
Nov 9, 2012
Messages
608
Reaction score
164
This just looks like leaftown, the same thing Fraysa did. How does copying over from another src and releasing help? On top of that, you still use MapleServerHandler (and you limit this post to the 'PacketProcessor'). You even pasted entire files instead of the netty specific code? wtf is this, how is it even a release?
Never looked at leaftown, Fraysa did not include packet encoder and decoder. These are the files needed in order for netty to work on a Solaxia/ Moople server.

"n top of that, you still use MapleServerHandler (and you limit this post to the 'PacketProcessor'). "
What?
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
Never looked at leaftown, Fraysa did not include packet encoder and decoder. These are the files needed in order for netty to work on a Solaxia/ Moople server.

You could of very easily pasted the specific code related to netty. Instead, you posted an entire file which helps no one because they could and most likely did modify these files previously.

It would also be good of you to explain how people could use netty without PacketProcessor. If this was specific to MapleSolaxia, add that to your title.

Again, you should also point out what people will achieve by replacing mina with netty.
 
Joined
Nov 9, 2012
Messages
608
Reaction score
164
You could of very easily pasted the specific code related to netty. Instead, you posted an entire file which helps no one because they could and most likely did modify these files previously.

I don't know how Solaxia works, but where is the login & world server initialization? Do they not need netty or do you not have them in Solaxia? (I just glanced over the code you posted)

It would also be good of you to explain how people could use netty without PacketProcessor. If this was specific to MapleSolaxia, add that to your title.

Again, you should also point out what people will achieve by replacing mina with netty.
Well they can run a text difference and see the changes made to those files. Its honestly not that hard, Solaxia is Moople Based, Server.java and ChannelServer.java will open up all the ports needed for the game. PacketProcessor is still used, so I dont know what u are talking about. it should be obvious to average java programmer what would be the .
 
Last edited by a moderator:
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
PacketProcessor is still used, so I dont know what u are talking about.
What does that mean? PacketProcessor was such an ugly way of handling packets... I don't know many people that use this past the obvious repacks in this section.

it should be obvious to average java programmer what would be the .

I don't see why it would be obvious to an average programmer? I had no idea the differences when I first thought about switching to netty. I can only assume you have no idea what you are talking about because you had to give me a link to google.

Did you look into other options besides netty (yes, there are more) or did you switch to netty because its what was released by Fraysa?
 
Joined
Nov 9, 2012
Messages
608
Reaction score
164
What does that mean? PacketProcessor was such an ugly way of handling packets... I don't know many people that use this past the obvious repacks in this section.



I don't see why it would be obvious to an average programmer? I had no idea the differences when I first thought about switching to netty. I can only assume you have no idea what you are talking about because you had to give me a link to google.

Did you look into other options besides netty (yes, there are more) or did you switch to netty because its what was released by Fraysa?
If you really want to know since you are so lazy to look at code before posting anything is that its more manageable, and it is also better a processing data because of the Channelpipline. Another thing if you read this part "One thing I did notice was that server transitions do happen at a faster pace, let me know if you guys also witness the same thing". You would start to think that there might be some differences between Mina and Netty. Another thing is that I always hear it was better than Mina, and I have been hearing it for a long time. Netty seems to be the popular option with some of the developers here. Also its not ugly, that is your programming preference. Honestly I found Netty to be quite easy to use.
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
If you really want to know since you are so lazy to look at code before posting anything is that its more manageable
What do you mean by "it's more managable? Looking at your code would tell me that your implementation of netty could be better.

Another thing if you read this part "One thing I did notice was that server transitions do happen at a faster pace, let me know if you guys also witness the same thing". You would start to think that there might be some differences between Mina and Netty.

No where did I say there is no differences of mina and netty. I simply asked you to explain the benefits or cons of switching to netty from mina. You may see miniscule transition speed improvements, but I do not believe it's anything massive unless the previous networking was just coded like poop with mina. (Which wasn't really the case with odin)

Another thing is that I always hear it was better than Mina, and I have been hearing it for a long time.
I do not believe 'better' is the proper word. Netty is mina built from the ground up to address the core issues and create a more simplified io. Mina has some more complex features that netty does not, but your choice of io depends on what you need a networking api for. There are definitely cases where mina will be more beneficial to your project than netty, and vice versa.

Netty seems to be the popular option with some of the developers here.
Very few developers in f425 use netty. By very few, I believe its most likely under 10. I do believe you should be doing research before just jumping to netty because there are more alternatives such as Grizzly, Coral, or you could even make your own io which can surpass any of these api because its built to your specific project.

Also its not ugly, that is your programming preference.
If you believe having a file for every handler is not ugly, I feel for you.

Honestly I found Netty to be quite easy to use.
Netty is very easy to use imo, it is very clean, and anyone could read the docs to get use to it.

I don't mean to bash this release but I feel like it was very poorly created. I do not believe you understand the benefit of using netty over mina. I do not like the fact you released full files insetad of explaining to the forum members how to add the netty code and remove the mina code. I despise the fact you are saying "If you can't figure this out, don't use it". You should not be releasing something if you aren't going to make it available for an idiot to use it. Overall I think your implementation is not very good and this release is sub-par.
 
Last edited:
Junior Spellweaver
Joined
Apr 5, 2008
Messages
152
Reaction score
65
You should not be releasing something if you aren't going to make it available for an idiot to use it.
If that's the case, nothing useful would ever be released. That may explain why this section has gone downhill so much.
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
If that's the case, nothing useful would ever be released. That may explain why this section has gone downhill so much.

Releasing this isn't helping this section at all. Releasing this but giving specific code related to netty and telling people how to use it is helping. It's releases like this that made people get on the "I am not helping you, if you can't figure this release out, too bad.".
 
Junior Spellweaver
Joined
Apr 5, 2008
Messages
152
Reaction score
65
Releasing this isn't helping this section at all. Releasing this but giving specific code related to netty and telling people how to use it is helping. It's releases like this that made people get on the "I am not helping you, if you can't figure this release out, too bad.".
Actually, its not releases like this that got people on that bandwagon. It's people like you responding to people who try to help the community out. That's why I personally never release much. Because I hate the dimwits out there who can't see a good release and flame them.

I actually find this release helpful because Netty is a major overhaul of the networking code. Why just post snippets of the files when the whole file gets refactored? That makes no sense to me.
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
Actually, its not releases like this that got people on that bandwagon. It's people like you responding to people who try to help the community out. That's why I personally never release much. Because I hate the dimwits out there who can't see a good release and flame them.

I actually find this release helpful because Netty is a major overhaul of the networking code. Why just post snippets of the files when the whole file gets refactored? That makes no sense to me.

This is not a good release. This will not help 99% of the community. You do not need to refactor an entire file(s) to implement netty, so stop talking out of your butt. It would take no longer than 5-10minutes to post snippets of netty code explaining where to put it which would help the 99% that this wouldn't. This release is absolute poop just because it's limited to 'maplesolaxia', people using the packetproccesor, and whoever has not modified any files that was posted in the op.
 
Junior Spellweaver
Joined
Apr 5, 2008
Messages
152
Reaction score
65
This is not a good release. This will not help 99% of the community. You do not need to refactor an entire file(s) to implement netty, so stop talking out of your butt. It would take no longer than 5-10minutes to post snippets of netty code explaining where to put it which would help the 99% that this wouldn't. This release is absolute poop just because it's limited to 'maplesolaxia', people using the packetproccesor, and whoever has not modified any files that was posted in the op.

Somehow I doubt you have anything worthwhile to release which is why you never release anything.
I agree that this won't help 99% of the community, because all of the good developers have left.

Yes, you would have to refactor major parts of several files to implement Netty. So how about you stop talking out of your butt for once. This release wasn't meant to help the spoofed leachers. It was meant to help people who would actually have an interest in implementing Netty. Also, who said anything about this being limited to the MapleSolaxia source? This default implementation would work just fine for Moople as well, which is what a majority of people deem as a "good" source.

As for your comment on my not having anything worthwhile to release, you are completely wrong. I've done some major work on v83 sources, and have fixed major longstanding issues with Odin that a majority of servers are plagued with. I will never release them however because of how toxic this community is in general.
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
I agree that this won't help 99% of the community, because all of the good developers have left.

Yes, you would have to refactor major parts of several files to implement Netty. So how about you stop talking out of your butt for once.

I count 13 functions I had to modify and 2 I had to add to implement netty (This includes Master Server, Login, World, Channel, Talk, Cash Shop, and Farm). That is not something I would consider as a major refactor.

This release wasn't meant to help the spoofed leachers. It was meant to help people who would actually have an interest in implementing Netty.

I'm positive very few members in this section even know what netty is.

Also, who said anything about this being limited to the MapleSolaxia source? This default implementation would work just fine for Moople as well, which is what a majority of people deem as a "good" source.

Okay, I will rephrase that you limit it to any source that is a moople fork. (Not really a good source considering kevin removed most of the things odin did right)

As for your comment on my not having anything worthwhile to release, you are completely wrong. I've done some major work on v83 sources, and have fixed major longstanding issues with Odin that a majority of servers are plagued with. I will never release them however because of how toxic this community is in general.

Community is toxic, so is all of ragezone, so is every forum you will ever visit.
Doing any work on v83 should make you feel deprecated as a coder entirely. The community needs to move forward, not stay 3 years behind the curve.
 
Interesting...
Loyal Member
Joined
Oct 25, 2008
Messages
1,372
Reaction score
604
I'm positive very few members in this section even know what netty is.

Then obviously this release isn't intended for those people. Just because it's unknown to people, it doesn't make this release useless. You could argue that the majority of people have never used IDA, that doesn't make tutorials on them useless just because "few members in this section even know what IDA is".

Okay, I will rephrase that you limit it to any source that is a moople fork. (Not really a good source considering kevin removed most of the things odin did right)

The point he made was that Moople is considered good by the community, NOT that he himself considers Moople good. There's nothing wrong with making a release specific to a fork of another source because there are plenty of "For Moople" releases and not once have I read you bash OP for doing so. Code bases change over time and if you happen to make something on Moople and release it, what is the crime in clearly indicating the base you started off with? It doesn't necessarily lock the release to that code base only.

Community is toxic, so is all of ragezone, so is every forum you will ever visit.
Doing any work on v83 should make you feel deprecated as a coder entirely. The community needs to move forward, not stay 3 years behind the curve.

From this thread only, the only one being toxic here is you. You had arguably valid points at first but then it went downhill quickly and now it just looks like you're bitching left and right about:
  • The release being full files vs snippets
  • The release not being up to your "code standards"
  • OP not explaining the differences between netty and mina
  • That this release is solaxia-based
  • That this release is why the section has gone downhill
  • That RageZone is toxic
  • That another developer has chosen to work on older version rather than current version

Also, if you even read what he said, he said he made improvements to the actual back-end of Odin. That means his work is irrespective of the version. If he updates to the current version, or any other version, his source will continue to carry those improvements. It's not "v83 work" just because the source's version is 83.
 
Skilled Illusionist
Joined
Dec 20, 2011
Messages
313
Reaction score
115
Yeah you are right this thread is too much drama. It could of been released in a much more helpful manner but to each their own.

Then obviously this release isn't intended for those people. Just because it's unknown to people, it doesn't make this release useless. You could argue that the majority of people have never used IDA, that doesn't make tutorials on them useless just because "few members in this section even know what IDA is".

A tutorial on how to use IDA is a great release. A thread that requires you to copy entire files guaranteeing you many compiling errors is not. There is definitely a difference in the context of my quote and when you reference IDA.


The point he made was that Moople is considered good by the community, NOT that he himself considers Moople good. There's nothing wrong with making a release specific to a fork of another source because there are plenty of "For Moople" releases and not once have I read you bash OP for doing so. Code bases change over time and if you happen to make something on Moople and release it, what is the crime in clearly indicating the base you started off with? It doesn't necessarily lock the release to that code base only.

I have no issue with releasing something for a specific source, version, repack, or fork. The issue I see is even limiting to these options is a hassle with the compile errors almost everyone will see unless they modified or didn't modify their files exactly the way the OP did.

Sure, someone could read through these files and take out the netty related code so it isn't limited to moople, but anyone willing to do that can probably just create a better implementation of netty themselves in the time it takes to comb through these files.

From this thread only, the only one being toxic here is you. You had arguably valid points at first but then it went downhill quickly and now it just looks like you're bitching left and right about:
  • The release being full files vs snippets
  • The release not being up to your "code standards"
  • OP not explaining the differences between netty and mina
  • That this release is solaxia-based
  • That this release is why the section has gone downhill
  • That RageZone is toxic
  • That another developer has chosen to work on older version rather than current version

Also, if you even read what he said, he said he made improvements to the actual back-end of Odin. That means his work is irrespective of the version. If he updates to the current version, or any other version, his source will continue to carry those improvements. It's not "v83 work" just because the source's version is 83.

Gotta have drama somewhere... it did get a bit too far though.
 
Last edited:
Experienced Elementalist
Joined
Mar 12, 2015
Messages
238
Reaction score
43
As a guy who is just starting an IT study (software engineering starting with java), let me say this.

most of the posts on ragezone are:
"here is some script, idgaf if you dont know what to do with it. not my problem."
and when somebody asks a legit question, they are like:
"omg u noob, y u no understand dis?"

But people please understand, we HAVE to learn it somewhere. Not every guy here likes to make his hobby his job.
My opinion is that the community is failing because nobody wants to explain/share his knowledge.
To all the people saying:
"I have worked hard to achieve this knowledge!!!!11!2@"
You gained most knowledge from other people, you too started out like a scrub who knew nothing, maybe not on ragezone, but perhaps on some other forum or place. If people kept saying "duck you im not explaining it to you" to you, you wouldn't be here.
ofc some of your knowledge has been forged by yourself, you have tested things and found out about some things on your own, but please, share your experience.
So always before answering a newbie with a toxic reply, think this:
"I was a noob once, and people were so generous to teach me."

Sorry for the rant, thanks for listening.
I don't mean to upset anybody or to start any flame war.
 
Elite Diviner
Joined
Mar 24, 2015
Messages
426
Reaction score
416
As a guy who is just starting an IT study (software engineering starting with java), let me say this.

most of the posts on ragezone are:
"here is some script, idgaf if you dont know what to do with it. not my problem."
and when somebody asks a legit question, they are like:
"omg u noob, y u no understand dis?"

But people please understand, we HAVE to learn it somewhere. Not every guy here likes to make his hobby his job.
My opinion is that the community is failing because nobody wants to explain/share his knowledge.
To all the people saying:
"I have worked hard to achieve this knowledge!!!!11!2@"
You gained most knowledge from other people, you too started out like a scrub who knew nothing, maybe not on ragezone, but perhaps on some other forum or place. If people kept saying "duck you im not explaining it to you" to you, you wouldn't be here.
ofc some of your knowledge has been forged by yourself, you have tested things and found out about some things on your own, but please, share your experience.
So always before answering a newbie with a toxic reply, think this:
"I was a noob once, and people were so generous to teach me."

Sorry for the rant, thanks for listening.
I don't mean to upset anybody or to start any flame war.

Having things explained to you can be helpful, but I think that trying things for yourself is equally important. Just my two cents on your comment.
 
Joined
Aug 10, 2008
Messages
858
Reaction score
516
As a guy who is just starting an IT study (software engineering starting with java), let me say this.

most of the posts on ragezone are:
"here is some script, idgaf if you dont know what to do with it. not my problem."
and when somebody asks a legit question, they are like:
"omg u noob, y u no understand dis?"

But people please understand, we HAVE to learn it somewhere. Not every guy here likes to make his hobby his job.
My opinion is that the community is failing because nobody wants to explain/share his knowledge.
To all the people saying:
"I have worked hard to achieve this knowledge!!!!11!2@"
You gained most knowledge from other people, you too started out like a scrub who knew nothing, maybe not on ragezone, but perhaps on some other forum or place. If people kept saying "duck you im not explaining it to you" to you, you wouldn't be here.
ofc some of your knowledge has been forged by yourself, you have tested things and found out about some things on your own, but please, share your experience.
So always before answering a newbie with a toxic reply, think this:
"I was a noob once, and people were so generous to teach me."

Sorry for the rant, thanks for listening.
I don't mean to upset anybody or to start any flame war.

I think the first mistake people make is trusting the information they see on RageZone and similar forums blindly. Books are far more reliable resources than people who post on RageZone. The credibility of any individual person that makes a statement explaining something is merely speculation on the way they understand something to work: which can actually be fundamentally wrong. I generally like to say that there are enough resources available here to become good at MapleStory development. Please know that MapleStory development is only extremely focused on server emulation and does not represent translatable skills to programming with a larger amount of freedom in itself. Being good/mediocre pretty much translates to being a(n) middle/amateur level programmer in non-MapleStory applications.

Most of the community back when I was first starting was incredibly toxic. The moderator team back then favored the reigning cliques which resulted in a very large amount of shitposting and I wouldn't really consider it an environment for learning. People even back then misrepresented programming concepts and I learned nearly all I know today from documentation and books which were written by professionals.

@OT: I think the direction this thread is heading is pretty useless since the bickering itself serves no purpose other than to be inflammatory.
 
not a programmer
Joined
Mar 30, 2015
Messages
532
Reaction score
62
@Five
There are many tools or webtools like this.. IDE has history too.


Not too hard to run a text difference.
 
Last edited:
Back
Top