Added initial version

This commit is contained in:
Ceikry 2021-03-07 20:37:32 -06:00
commit b452bd670c
13290 changed files with 1178433 additions and 0 deletions

View file

@ -0,0 +1,17 @@
apply plugin: 'application'
archivesBaseName = 'managementserver'
mainClassName = 'ms.Management'
dependencies {
implementation 'com.google.guava:guava:29.0-jre'
implementation 'mysql:mysql-connector-java:8.0.21'
}
jar {
manifest {
attributes 'Main-Class': 'ms.Management'
}
from { configurations.compileClasspath.collect { it.isDirectory() ? it : zipTree(it) } }
}

View file

@ -0,0 +1,102 @@
ms/system/ShutdownSequence.java
ms.system.ShutdownSequence
ms/world/GameServer.java
ms.world.GameServer
ms/ServerConstants.java
ms.ServerConstants
ms/world/WorldDatabase.java
ms.world.WorldDatabase
ms/system/util/StringUtils.java
ms.system.util.StringUtils
ms/net/event/PacketWriteEvent.java
ms.net.event.PacketWriteEvent
ms.net.event.PacketWriteEvent$1
ms/net/event/RegistryWriteEvent.java
ms.net.event.RegistryWriteEvent
ms/net/EventProducer.java
ms.net.EventProducer
ms/system/mysql/SQLTable.java
ms.system.mysql.SQLTable
ms/classloader/ClassLoadServer.java
ms.classloader.ClassLoadServer
ms/system/mysql/SQLEntryHandler.java
ms.system.mysql.SQLEntryHandler
ms/system/mysql/SQLColumn.java
ms.system.mysql.SQLColumn
ms/net/IoEventHandler.java
ms.net.IoEventHandler
ms/world/info/UIDInfo.java
ms.world.info.UIDInfo
ms/Management.java
ms.Management
ms.Management$1
ms.Management$2
ms.Management$3
ms.Management$4
ms.Management$5
ms.Management$6
ms.Management$7
ms.Management$8
ms/system/mysql/WorldListSQLHandler.java
ms.system.mysql.WorldListSQLHandler
ms/world/PlayerSession.java
ms.world.PlayerSession
ms/system/PunishmentStorage.java
ms.system.PunishmentStorage
ms/net/packet/WorldPacketRepository.java
ms.net.packet.WorldPacketRepository
ms/system/mysql/SQLManager.java
ms.system.mysql.SQLManager
ms/classloader/WorkerThread.java
ms.classloader.WorkerThread
ms/net/event/PacketReadEvent.java
ms.net.event.PacketReadEvent
ms/system/communication/CommunicationInfo.java
ms.system.communication.CommunicationInfo
ms/net/event/HSWriteEvent.java
ms.net.event.HSWriteEvent
ms/net/event/HSReadEvent.java
ms.net.event.HSReadEvent
ms/net/packet/PacketHeader.java
ms.net.packet.PacketHeader
ms/world/info/WorldInfo.java
ms.world.info.WorldInfo
ms/net/producer/RegistryEventProducer.java
ms.net.producer.RegistryEventProducer
ms/world/info/Response.java
ms.world.info.Response
ms/net/IoWriteEvent.java
ms.net.IoWriteEvent
ms/system/util/Command.java
ms.system.util.Command
ms/system/communication/ClanRepository.java
ms.system.communication.ClanRepository
ms/system/util/EncryptionManager.java
ms.system.util.EncryptionManager
ms.system.util.EncryptionManager$BCrypt
ms/net/NioReactor.java
ms.net.NioReactor
ms/system/communication/ClanRank.java
ms.system.communication.ClanRank
ms/system/OperatingSystem.java
ms.system.OperatingSystem
ms/net/IoSession.java
ms.net.IoSession
ms/system/util/TaskExecutor.java
ms.system.util.TaskExecutor
ms/world/info/CountryFlag.java
ms.world.info.CountryFlag
ms/net/event/RegistryReadEvent.java
ms.net.event.RegistryReadEvent
ms/net/producer/HSEventProducer.java
ms.net.producer.HSEventProducer
ms/net/IoReadEvent.java
ms.net.IoReadEvent
ms/net/producer/PacketEventProducer.java
ms.net.producer.PacketEventProducer
ms/net/packet/IoBuffer.java
ms.net.packet.IoBuffer
ms/system/util/ByteBufferUtils.java
ms.system.util.ByteBufferUtils
ms/system/util/PlayerDatabaseGen.java
ms.system.util.PlayerDatabaseGen

View file

@ -0,0 +1,172 @@
package ms;
import ms.classloader.ClassLoadServer;
import ms.net.NioReactor;
import ms.net.packet.WorldPacketRepository;
import ms.system.ShutdownSequence;
import ms.system.mysql.SQLManager;
import ms.system.util.Command;
import ms.world.GameServer;
import ms.world.PlayerSession;
import ms.world.WorldDatabase;
import java.io.IOException;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.util.Scanner;
/**
* The main class.
* @author Emperor
*
*/
public final class Management {
/**
* If the shutdown hook is active.
*/
public static boolean active = true;
/**
* The commands.
*/
private static final Command[] COMMANDS = {
new Command("-commands", "Print a list of all commands.") {
@Override
public void run(String...args) {
for (Command c : COMMANDS) {
System.out.println("Command " + c.getName() + ": " + c.getInfo());
}
}
},
new Command("-s", "Safely shuts down the server.") {
@Override
public void run(String...args) {
System.out.println("Shutting down Management server...");
ShutdownSequence.shutdown();
}
},
new Command("-debug", "Debug world info.") {
@Override
public void run(String...args) {
System.out.println("---------------------------------------------");
for (GameServer server : WorldDatabase.getWorlds()) {
if (server != null) {
System.out.println("World [id=" + server.getInfo().getWorldId() + ", IP=" + server.getInfo().getAddress() + ", country=" + server.getInfo().getCountry() + ", members=" + server.getInfo().isMembers() + ", players=" + server.getPlayers().size() + ", active=" + server.isActive() + "].");
}
}
}
},
new Command("-pinfo", "Debugs player information (usage: -pinfo emperor).") {
@Override
public void run(String...args) {
String name = args[1];
PlayerSession player = WorldDatabase.getPlayer(name);
if (player == null) {
System.out.println("Player " + name + " was not registered!");
return;
}
System.out.println("Player [name=" + name + ", world=" + player.getWorldId() + ", active=" + player.isActive() + "].");
}
},
new Command("-update", "Calls an update on all the game servers (-update -1 to cancel).") {
@Override
public void run(String...args) {
int ticks = Integer.parseInt(args[1]);
for (GameServer server : WorldDatabase.getWorlds()) {
if (server != null && server.isActive()) {
WorldPacketRepository.sendUpdate(server, ticks);
}
}
}
},
new Command("-reloadconfig", "Reloads the configurations of all worlds.") {
@Override
public void run(String... args) {
for (GameServer server : WorldDatabase.getWorlds()) {
if(server == null) {
continue;
}
WorldPacketRepository.sendConfigReload(server);
}
}
},
new Command("-rlcache", "Reloads launcher/client resource cache") {
@Override
public void run(String... args) {
ClassLoadServer.resetResourceCache();
System.out.println("Reloaded resource cache!");
}
},
new Command("-kick", "Kicks a player from the MS (not ingame).") {
@Override
public void run(String... args) {
String name = args[1];
PlayerSession player = WorldDatabase.getPlayer(name);
if (player == null) {
System.out.println("Player " + name + " was not registered!");
return;
}
player.getWorld().getPlayers().remove(name);
player.setWorldId(0);
System.out.println("Kicked player " + name + "!");
}
}
};
/**
* The main method.
* @param args The arguments cast on runtime.
* @throws Throwable When an exception occurs.
*/
public static void main(String...args) throws Throwable {
if (!isLocallyHosted(ServerConstants.HOST_ADDRESS)) {
System.err.println("WARNING: Configure host address in server constants!");
}
System.out.println("-------- 530 Management server --------");
System.out.println("Starting up...");
SQLManager.init();
//NioReactor.configure(ServerConstants.PORT).start();
NioReactor.configure(5555).start();
new ClassLoadServer().start();
Runtime.getRuntime().addShutdownHook(new ShutdownSequence());
System.out.println("Status: ready.");
System.out.println("Use -commands for a list of commands!");
Scanner s = new Scanner(System.in);
while (s.hasNext()) {
try {
String command = s.nextLine();
if (!command.startsWith("-")) {
continue;
}
String[] arguments = command.split(" ");
command = arguments[0];
for (Command c : COMMANDS) {
if (c.getName().equals(command)) {
System.out.println("Handling command \"" + command + "\"!");
c.run(arguments);
}
}
} catch (Throwable t) {
t.printStackTrace();
}
}
s.close();
}
/**
* Checks if the Management server is locally hosted.
* @return {@code True} if so.
* @throws IOException When an I/O exception occurs.
*/
private static boolean isLocallyHosted(String ip) throws IOException {
InetAddress address = InetAddress.getByName(ip);
if (address.isAnyLocalAddress() || address.isLoopbackAddress()) {
return true;
}
return NetworkInterface.getByInetAddress(address) != null;
}
}

View file

@ -0,0 +1,84 @@
package ms;
import ms.system.OperatingSystem;
/**
* Holds constants for the management server.
* @author v4rg
*
*/
public final class ServerConstants {
/**
* The port to be used for communications.
*/
public static final String SERVER_NAME = "2009Scape";
/**
* The port to be used for communications.
*/
public static final int PORT = 5555;
/**
* The maximum amount of worlds.
*/
public static final int WORLD_LIMIT = 10;
/**
* The world switching delay in milliseconds.
*/
public static final long WORLD_SWITCH_DELAY = 20_000l;
/**
* The address of the Management server.
*/
public static final String HOST_ADDRESS = "127.0.0.1";
/**
* The store path.
*/
public static final String STORE_PATH = "./store/";
/**
* The maximum amount of players per world.
*/
public static final int MAX_PLAYERS = (1 << 11) - 1;
/**
* The operating system of the management server
*/
public static final OperatingSystem OS = System.getProperty("os.name").toUpperCase().contains("WIN") ? OperatingSystem.WINDOWS : OperatingSystem.UNIX;
/**
* The administrators.
*/
public static final String[] ADMINISTRATORS = {
"redsparr0w",
};
public static final String[] DATABASE_NAMES = {
"server",
"global",
};
/**
* Stops from instantiating.
*/
private ServerConstants() {
/*
* empty.
*/
}
/**
* Fixes a path to a specified operating system
* @param operatingSystem The os type.
* @param path The path.
* @return The fixed path.
*/
public static String fixPath(OperatingSystem operatingSystem, String path) {
if (operatingSystem == null)
operatingSystem = OS;
return operatingSystem == OperatingSystem.WINDOWS ? path.replace("/","\\") : path.replace("\\","/");
}
}

View file

@ -0,0 +1,109 @@
package ms.classloader;
import ms.Management;
import ms.ServerConstants;
import java.io.File;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.file.Files;
import java.util.HashMap;
/**
* ClassLoadServer
* @author Clayton Williams (Hope)
*/
public class ClassLoadServer extends Thread {
/**
* Listening port
*/
private static final int PORT = 5050;
/**
* serverSocket for incoming connections
*/
private ServerSocket serverSocket = null;
/**
* Holds classes and resources already added in the server
*/
protected static HashMap<String, byte[]> resourceCache = new HashMap<String, byte[]>();
/**
* New socket
* @throws UnknownHostException
* @throws IOException
*/
public ClassLoadServer() {
try {
serverSocket = new ServerSocket(PORT);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Listen for incoming connections
*/
@Override
public void run() {
resetResourceCache();
// System.out.println("Listening on port " + PORT + "...");
while (Management.active) {
Socket clientSocket = null;
try {
clientSocket = serverSocket.accept();
//System.out.println("New Connection from : " + clientSocket.getInetAddress());
WorkerThread wcs = new WorkerThread(clientSocket);
wcs.start();
} catch (Exception e) {}
}
}
/**
* Loads resources recursively from a starting root directory and adds the file bytes to our cache
* @param path
* @param pathRoot
*/
private static void loadResources(String path, String pathRoot) {
try {
path = ServerConstants.fixPath(null, path);
pathRoot = ServerConstants.fixPath(null, pathRoot);
File root = new File(path);
File[] list = root.listFiles();
if (list == null) return;
for (File f : list) {
if (f.isDirectory()) {
loadResources(f.getAbsolutePath(), pathRoot);
} else {
if (f.exists()) {
if (!f.getName().startsWith(".")) {
byte[] bytes = Files.readAllBytes(f.toPath());
String name = ServerConstants.fixPath(null, f.getAbsolutePath().substring(f.getAbsolutePath().lastIndexOf(pathRoot)));
//System.out.println("[ClassLoadServer] Loaded resource '" + f.getName() +
// "' Size: " + NumberFormat.getInstance().format(bytes.length) + " bytes");
resourceCache.put(name, bytes);
}
}
}
}
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* Empties the class cache and loads resources
*/
public static void resetResourceCache() {
resourceCache.clear();
//loadResources("bin/org/keldagrim/launcher/", "org/arios/launcher/");
//loadResources("resources/", "org/keldagrim/launcher/");
//System.out.println("Loaded all resources!");
}
}

View file

@ -0,0 +1,136 @@
package ms.classloader;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.Socket;
import java.util.Map;
import ms.ServerConstants;
import ms.system.OperatingSystem;
/**
* Worker thread for launcher connections
* @author Clayton Williams (Hope)
*
*/
public class WorkerThread extends Thread {
/**
* Our launcher connection
*/
private Socket launcher = null;
/**
* Input stream from launcher
*/
private ObjectInputStream is = null;
/**
* Output stream to launcher
*/
private ObjectOutputStream os = null;
/**
* The user's operating system type
*/
private OperatingSystem operatingSystem = OperatingSystem.WINDOWS;
/**
* Creates a new worker thread to handle the launcher requests
* @param socket
* @param classesCache
* @throws IOException
*/
public WorkerThread(Socket launcher) throws IOException {
super();
this.launcher = launcher;
try {
os = new ObjectOutputStream(new BufferedOutputStream(this.launcher.getOutputStream()));
os.flush();
is = new ObjectInputStream(new BufferedInputStream(launcher.getInputStream()));
} catch (IOException ioe) {
this.launcher.close();
}
}
/**
* Input Stream handler
*/
@SuppressWarnings("unused") //saving resource type for later
public void run() {
byte opcode = -1;
try {
while (true) {
opcode = is.readByte();
switch(opcode) {
case 1: //requests revision
operatingSystem = is.readUTF().contains("UNIX") ? OperatingSystem.UNIX : OperatingSystem.WINDOWS;
os.reset();
os.writeInt(ClassLoadServer.resourceCache.size());//#customresources
for (Map.Entry<String, byte[]> hm : ClassLoadServer.resourceCache.entrySet()) {
os.writeUTF(ServerConstants.fixPath(operatingSystem, (String) hm.getKey()));
os.writeInt(hm.getValue().length);
}
os.writeByte(1); //number of custom resources
//followed by 2 strings
//System.err.println(operatingSystem.name());
os.writeUTF(ServerConstants.fixPath(operatingSystem, "ms/launcher/arios-gamepack-530.jar"));
os.writeUTF("BINARY");
os.flush();
break;
case 2: //resource request
String resourceName = ServerConstants.fixPath(null, is.readUTF());
String resourceType = is.readUTF();
if (ClassLoadServer.resourceCache.containsKey(resourceName)) {
sendResource(ClassLoadServer.resourceCache.get(resourceName));
//System.out.println("Sent Resource: " + resourceName);
} else {
sendOpcode(-1);
//System.out.println("Could not send resource '" + resourceName + "'");
}
break;
default:
System.out.println("Unhandled opcode=" + opcode);
break;
}
}
} catch(Exception e) {
//System.out.println("Client force disconnect.");
} finally {
try {
is.close();
os.close();
launcher.close();
} catch (Exception e) {}
}
}
/**
* send a byte array packet to the client
* @exception IOException file read error.
*/
protected void sendResource(byte [] bytes) throws IOException {
os.reset();
os.writeByte(2);
os.writeInt(bytes.length);
for (int i = 0; i < bytes.length; i++) {
os.writeByte(bytes[i]);
}
os.flush();
}
/**
* Send no data opcodes
* @param opcode
* @throws IOException
*/
protected void sendOpcode(int opcode) throws IOException {
os.reset();
os.writeByte(opcode);
os.flush();
}
}

View file

@ -0,0 +1,28 @@
package ms.net;
import java.nio.ByteBuffer;
/**
* Used for producing I/O events.
* @author Emperor
*
*/
public interface EventProducer {
/**
* Produces a new read event.
* @param session The session.
* @param buffer The buffer to read.
* @return The read event handler.
*/
IoReadEvent produceReader(IoSession session, ByteBuffer buffer);
/**
* Produces a new writing event.
* @param session The session.
* @param context The context.
* @return The write event handler.
*/
IoWriteEvent produceWriter(IoSession session, Object context);
}

View file

@ -0,0 +1,106 @@
package ms.net;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ClosedChannelException;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.util.concurrent.ExecutorService;
/**
* I/O event handling.
* @author Emperor
*
*/
public final class IoEventHandler {
/**
* The executor service.
*/
private final ExecutorService service;
/**
* Constructs a new {@code IoEventHandler}.
* @param service The executor service used for handling events.
*/
public IoEventHandler(ExecutorService service) {
this.service = service;
}
/**
* Called when making a new connection.
* @param key The selection key.
*/
public void connect(SelectionKey key) {
/*
* empty.
*/
}
/**
* Used for accepting a new connection.
* @param key The selection key.
* @param selector The selector.
* @throws IOException When an I/O exception occurs.
*/
public void accept(SelectionKey key, Selector selector) throws IOException {
SocketChannel sc = ((ServerSocketChannel) key.channel()).accept();
sc.configureBlocking(false);
sc.socket().setTcpNoDelay(true);
sc.register(selector, SelectionKey.OP_READ);
}
/**
* Reads the incoming packet data.
* @param key The selection key.
* @throws IOException When an I/O exception occurs.
*/
public void read(SelectionKey key) throws IOException {
ReadableByteChannel channel = (ReadableByteChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(100_000);
IoSession session = (IoSession) key.attachment();
if (channel.read(buffer) == -1) {
System.out.println("Existing session disconnected - likely portscanner or server status checker.");
key.cancel();
return;
}
buffer.flip();
if (session == null) {
key.attach(session = new IoSession(key, service));
}
service.execute(session.getProducer().produceReader(session, buffer));
}
/**
* Writes the outgoing packet data.
* @param key The selection key.
*/
public void write(SelectionKey key) {
IoSession session = (IoSession) key.attachment();
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
session.write();
}
/**
* Disconnects a connection.
* @param key The selection key.
* @param t The occurred exception (if any).
*/
public void disconnect(SelectionKey key, Throwable t) {
try {
IoSession session = (IoSession) key.attachment();
String cause = "" + t;
if (t != null && !(t instanceof ClosedChannelException || cause.contains("De externe host") || cause.contains("De software op uw") || cause.contains("An established connection was aborted") || cause.contains("An existing connection") || cause.contains("AsynchronousClose"))) {
t.printStackTrace();
}
if (session != null) {
session.disconnect();
}
} catch (Throwable e) {
e.printStackTrace();
}
}
}

View file

@ -0,0 +1,73 @@
package ms.net;
import java.nio.ByteBuffer;
/**
* Handles a reading event.
* @author Emperor
*
*/
public abstract class IoReadEvent implements Runnable {
/**
* The I/O session.
*/
private final IoSession session;
/**
* The buffer.
*/
private ByteBuffer buffer;
/**
* If the queued reading buffer was used (debugging purposes).
*/
protected boolean usedQueuedBuffer;
/**
* Constructs a new {@code IoReadEvent}.
* @param session The session.
* @param buffer The buffer to read from.
*/
public IoReadEvent(IoSession session, ByteBuffer buffer) {
this.session = session;
this.buffer = buffer;
}
@Override
public void run() {
try {
if (session.getReadingQueue() != null) {
buffer = session.getReadingQueue().put(buffer);
buffer.flip();
session.setReadingQueue(null);
usedQueuedBuffer = true;
}
read(session, buffer);
} catch (Throwable t) {
t.printStackTrace();
session.disconnect();
}
}
/**
* Queues the buffer until more data has been received.
* @param data The data that has been read already.
*/
public void queueBuffer(int...data) {
ByteBuffer queue = ByteBuffer.allocate(data.length + buffer.remaining() + 100_000);
for (int value : data) {
queue.put((byte) value);
}
queue.put(buffer);
session.setReadingQueue(queue);
}
/**
* Reads the data from the buffer.
* @param session The session.
* @param buffer The buffer to read from.
*/
public abstract void read(IoSession session, ByteBuffer buffer);
}

View file

@ -0,0 +1,351 @@
package ms.net;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import ms.ServerConstants;
import ms.net.producer.HSEventProducer;
import ms.world.GameServer;
/**
* Represents a connected I/O session.
* @author Emperor
*
*/
public class IoSession {
/**
* The handshake event producer.
*/
private static final EventProducer HANDSHAKE_PRODUCER = new HSEventProducer();
/**
* The selection key.
*/
private final SelectionKey key;
/**
* The executor service.
*/
private final ExecutorService service;
/**
* The event producer.
*/
private EventProducer producer = HANDSHAKE_PRODUCER;
/**
* The currently queued writing data.
*/
private List<ByteBuffer> writingQueue = new ArrayList<>();
/**
* The currently queued reading data.
*/
private ByteBuffer readingQueue;
/**
* The writing lock.
*/
private Lock writingLock = new ReentrantLock();
/**
* The name hash.
*/
private int nameHash;
/**
* The server key.
*/
private long serverKey;
/**
* The JS-5 encryption value.
*/
private int js5Encryption;
/**
* If the session is active.
*/
private boolean active = true;
/**
* The last ping time stamp.
*/
private long lastPing;
/**
* The address.
*/
private final String address;
/**
* The game server object for this session.
*/
private GameServer gameServer;
/**
* Constructs a new {@code IoSession}.
* @param key The selection key.
* @param service The executor service.
*/
public IoSession(SelectionKey key, ExecutorService service) {
this.key = key;
this.service = service;
String address = getRemoteAddress().replaceAll("/", "").split(":")[0];
if (address.equals("127.0.0.1")) {
address = ServerConstants.HOST_ADDRESS;
}
this.address = address;
}
/**
* Fires a write event created using the current event producer.
* @param context The event context.
*/
public void write(Object context) {
write(context, false);
}
/**
* Fires a write event created using the current event producer.
* @param context The event context.
* @param instant If the event should be instantly executed on this thread.
*/
public void write(Object context, boolean instant) {
if (context == null) {
throw new IllegalStateException("Invalid writing context!");
}
if (instant) {
producer.produceWriter(this, context).run();
return;
}
service.execute(producer.produceWriter(this, context));
}
/**
* Sends the packet data (without write event encoding).
* @param buffer The buffer.
*/
public void queue(ByteBuffer buffer) {
try {
writingLock.tryLock(1000L, TimeUnit.MILLISECONDS);
} catch (Exception e){
System.out.println(e);
writingLock.unlock();
return;
}
writingQueue.add(buffer);
writingLock.unlock();
write();
}
/**
* Handles the writing of all buffers in the queue.
*/
public void write() {
if (!key.isValid()) {
disconnect();
return;
}
writingLock.lock();
SocketChannel channel = (SocketChannel) key.channel();
try {
while (!writingQueue.isEmpty()) {
ByteBuffer buffer = writingQueue.get(0);
channel.write(buffer);
if (buffer.hasRemaining()) {
key.interestOps(key.interestOps() | SelectionKey.OP_WRITE);
break;
}
writingQueue.remove(0);
}
} catch (IOException e) {
disconnect();
}
writingLock.unlock();
}
/**
* Disconnects the session.
*/
public void disconnect() {
try {
if (!active) {
return;
}
active = false;
key.cancel();
SocketChannel channel = (SocketChannel) key.channel();
channel.socket().close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* Gets the IP-address (without the port).
* @return The address.
*/
public String getAddress() {
return address;
}
/**
* Gets the remote address of this session.
* @return The remote address, as a String.
*/
public String getRemoteAddress() {
try {
return ((SocketChannel) key.channel()).getRemoteAddress().toString();
} catch (IOException e) {
throw new IllegalStateException(e);
}
}
/**
* Gets the current event producer.
* @return The producer.
*/
public EventProducer getProducer() {
return producer;
}
/**
* Sets the event producer.
* @param producer The producer to set.
*/
public void setProducer(EventProducer producer) {
this.producer = producer;
}
/**
* Gets the queued reading data.
* @return The readingQueue.
*/
public ByteBuffer getReadingQueue() {
synchronized (this) {
return readingQueue;
}
}
/**
* Queues reading data.
* @param readingQueue The readingQueue to set.
*/
public void setReadingQueue(ByteBuffer readingQueue) {
synchronized (this) {
this.readingQueue = readingQueue;
}
}
/**
* Gets the writing lock.
* @return The writing lock.
*/
public Lock getWritingLock() {
return writingLock;
}
/**
* Gets the selection key.
* @return The selection key.
*/
public SelectionKey getKey() {
return key;
}
/**
* @return The active.
*/
public boolean isActive() {
return active;
}
/**
* @return The js5Encryption.
*/
public int getJs5Encryption() {
return js5Encryption;
}
/**
* @param js5Encryption The js5Encryption to set.
*/
public void setJs5Encryption(int js5Encryption) {
this.js5Encryption = js5Encryption;
}
/**
* Gets the lastPing.
* @return The lastPing.
*/
public long getLastPing() {
return lastPing;
}
/**
* Sets the lastPing.
* @param lastPing The lastPing to set.
*/
public void setLastPing(long lastPing) {
this.lastPing = lastPing;
}
/**
* Gets the nameHash.
* @return The nameHash.
*/
public int getNameHash() {
return nameHash;
}
/**
* Sets the nameHash.
* @param nameHash The nameHash to set.
*/
public void setNameHash(int nameHash) {
this.nameHash = nameHash;
}
/**
* Gets the serverKey.
* @return The serverKey.
*/
public long getServerKey() {
return serverKey;
}
/**
* Sets the serverKey.
* @param serverKey The serverKey to set.
*/
public void setServerKey(long serverKey) {
this.serverKey = serverKey;
}
/**
* Gets the gameServer value.
* @return The gameServer.
*/
public GameServer getGameServer() {
return gameServer;
}
/**
* Sets the gameServer value.
* @param gameServer The gameServer to set.
*/
public void setGameServer(GameServer gameServer) {
this.gameServer = gameServer;
}
}

View file

@ -0,0 +1,50 @@
package ms.net;
import java.nio.channels.CancelledKeyException;
/**
* Handles a writing event.
* @author Emperor
*/
public abstract class IoWriteEvent implements Runnable {
/**
* The I/O session.
*/
private final IoSession session;
/**
* The buffer.
*/
private final Object context;
/**
* Constructs a new {@code IoWriteEvent}.
* @param session The session.
* @param context The write event context.
*/
public IoWriteEvent(IoSession session, Object context) {
this.session = session;
this.context = context;
}
@Override
public void run() {
try {
write(session, context);
} catch (Throwable t) {
if (!(t instanceof CancelledKeyException)) {
t.printStackTrace();
}
session.disconnect();
}
}
/**
* Writes the data.
* @param session The session.
* @param context The write event context.
*/
public abstract void write(IoSession session, Object context);
}

View file

@ -0,0 +1,133 @@
package ms.net;
import ms.Management;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Handles (NIO-based) networking events using the reactor pattern.
* @author Emperor
*/
public final class NioReactor implements Runnable {
/**
* The executor service.
*/
private final ExecutorService service;
/**
* The selector
*/
private Selector selector;
/**
* The socket channel.
*/
private ServerSocketChannel channel;
/**
* The I/O event handling instance.
*/
private final IoEventHandler eventHandler;
/**
* If the reactor is running.
*/
private boolean running;
/**
* Constructs a new {@code NioReactor}.
* @param poolSize The pool size.
*/
private NioReactor(int poolSize) {
this.service = Executors.newSingleThreadScheduledExecutor();
this.eventHandler = new IoEventHandler(Executors.newFixedThreadPool(poolSize));
}
/**
* Creates and configures a new {@code NioReactor} with a pool size of 1.
* @param port The port.
* @return The {@code NioReactor} {@code Object}.
* @throws IOException When an I/O exception occurs.
*/
public static NioReactor configure(int port) throws IOException {
return configure(port, 1);
}
/**
* Creates and configures a new {@code NioReactor}.
* @param port The port.
* @param poolSize The amount of threads in the thread pool.
* @return The {@code NioReactor} {@code Object}.
* @throws IOException When an I/O exception occurs.
*/
public static NioReactor configure(int port, int poolSize) throws IOException {
NioReactor reactor = new NioReactor(poolSize);
reactor.channel = ServerSocketChannel.open();
reactor.selector = Selector.open();
reactor.channel.bind(new InetSocketAddress(port));
reactor.channel.configureBlocking(false);
reactor.channel.register(reactor.selector, SelectionKey.OP_ACCEPT);
return reactor;
}
/**
* Starts the reactor.
*/
public void start() {
running = true;
service.execute(this);
}
@Override
public void run() {
while (running && Management.active) {
try {
if(selector.select() > 0){
Iterator<SelectionKey> keys = selector.selectedKeys().iterator();
while (keys.hasNext()) {
SelectionKey key = keys.next();
keys.remove();
try {
if (!key.isValid() || !key.channel().isOpen()) {
key.cancel();
continue;
}
if (key.isAcceptable()) {
eventHandler.accept(key, selector);
}
if (key.isReadable()) {
eventHandler.read(key);
}
else if (key.isWritable()) {
eventHandler.write(key);
}
} catch (Throwable t) {
eventHandler.disconnect(key, t);
}
}
} else {
System.out.println("SLEEPING");
Thread.sleep(200);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
/**
* Terminates the reactor (once it's done processing current I/O events).
*/
public void terminate() {
running = false;
}
}

View file

@ -0,0 +1,54 @@
package ms.net.event;
import java.nio.ByteBuffer;
import ms.net.IoReadEvent;
import ms.net.IoSession;
import ms.world.WorldDatabase;
import ms.system.util.ByteBufferUtils;
/**
* Handles handshake read events.
* @author Emperor
*/
public final class HSReadEvent extends IoReadEvent {
/**
* The password used to verify
*/
private static final String PASSWORD = "0x14ari0SSbh98989910";
/**
* Constructs a new {@code HSReadEvent}.
* @param session The session.
* @param buffer The buffer.
*/
public HSReadEvent(IoSession session, ByteBuffer buffer) {
super(session, buffer);
}
@Override
public void read(IoSession session, ByteBuffer buffer) {
int opcode = buffer.get() & 0xFF;
switch (opcode) {
case 88:
String password = ByteBufferUtils.getString(buffer);
if (!password.equals(PASSWORD)) {
System.out.println("Password mismatch (attempt=" + password + ")!");
session.disconnect();
break;
}
session.write(opcode);
break;
case 255: // World list
int updateStamp = buffer.getInt();
WorldDatabase.sendUpdate(session, updateStamp);
break;
default:
System.err.println("Unhandled handshake opcode: " + opcode + ".");
session.disconnect();
break;
}
}
}

View file

@ -0,0 +1,38 @@
package ms.net.event;
import java.nio.ByteBuffer;
import ms.net.IoSession;
import ms.net.IoWriteEvent;
import ms.net.producer.RegistryEventProducer;
/**
* Handles Handshake write events.
* @author Emperor
*/
public final class HSWriteEvent extends IoWriteEvent {
/**
* The login event producer.
*/
private static final RegistryEventProducer REGISTRY_PRODUCER = new RegistryEventProducer();
/**
* Constructs a new {@code HSWriteEvent} {@code Object}.
* @param session The session.
* @param context The context.
*/
public HSWriteEvent(IoSession session, Object context) {
super(session, context);
}
@Override
public void write(IoSession session, Object context) {
ByteBuffer buffer = ByteBuffer.allocate(9);
buffer.put((byte) 14);
session.setProducer( REGISTRY_PRODUCER);
buffer.flip();
session.queue(buffer);
}
}

View file

@ -0,0 +1,101 @@
package ms.net.event;
import java.nio.ByteBuffer;
import ms.net.IoReadEvent;
import ms.net.IoSession;
import ms.net.packet.WorldPacketRepository;
/**
* Handles world packet reading events.
* @author Emperor
*
*/
public final class PacketReadEvent extends IoReadEvent {
/**
* The packet sizes.
*/
private static final int[] PACKET_SIZE = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1
};
/**
* Constructs a new {@code PacketReadEvent} {@code Object}.
* @param session The I/O session.
* @param buffer The buffer to read from.
*/
public PacketReadEvent(IoSession session, ByteBuffer buffer) {
super(session, buffer);
}
@Override
public void read(IoSession session, ByteBuffer buffer) {
int last = -1;
while (buffer.hasRemaining()) {
int opcode = buffer.get() & 0xFF;
if (opcode >= PACKET_SIZE.length) {
break;
}
int header = PACKET_SIZE[opcode];
int size = header;
if (header < 0) {
size = getPacketSize(buffer, opcode, header, last);
}
if (size == -1) {
break;
}
if (buffer.remaining() < size) {
switch (header) {
case -2:
queueBuffer(opcode, size >> 8, size);
break;
case -1:
queueBuffer(opcode, size);
break;
default:
queueBuffer(opcode);
break;
}
break;
}
byte[] data = new byte[size];
buffer.get(data);
last = opcode;
try {
WorldPacketRepository.handleIncoming(session, opcode, ByteBuffer.wrap(data));
} catch (Throwable t) {
t.printStackTrace();
}
}
}
/**
* Gets the packet size for the given opcode.
* @param buffer The buffer.
* @param opcode The opcode.
* @param header The packet header.
* @param last The last opcode.
* @return The packet size.
*/
private int getPacketSize(ByteBuffer buffer, int opcode, int header, int last) {
if (header == -1) {
if (buffer.remaining() < 1) {
queueBuffer(opcode);
return -1;
}
return buffer.get() & 0xFF;
}
if (header == -2) {
if (buffer.remaining() < 2) {
queueBuffer(opcode);
return -1;
}
return buffer.getShort() & 0xFFFF;
}
System.err.println("Invalid packet [opcode=" + opcode + ", last=" + last + ", queued=" + usedQueuedBuffer + "]!");
return -1;
}
}

View file

@ -0,0 +1,46 @@
package ms.net.event;
import java.nio.ByteBuffer;
import ms.net.IoSession;
import ms.net.IoWriteEvent;
import ms.net.packet.IoBuffer;
/**
* Handles world packet writing events.
* @author Emperor
*
*/
public final class PacketWriteEvent extends IoWriteEvent {
/**
* Constructs a new {@code PacketWriteEvent} {@code Object}.
* @param session The I/O session.
* @param context The packet context.
*/
public PacketWriteEvent(IoSession session, Object context) {
super(session, context);
}
@Override
public void write(IoSession session, Object context) {
IoBuffer b = (IoBuffer) context;
int size = b.toByteBuffer().position();
ByteBuffer buffer = ByteBuffer.allocate(1 + size + b.getHeader().ordinal());
buffer.put((byte) b.opcode());
switch (b.getHeader()) {
case NORMAL:
break;
case BYTE:
buffer.put((byte) size);
break;
case SHORT:
buffer.putShort((short) size);
break;
}
buffer.put((ByteBuffer) b.toByteBuffer().flip());
session.queue((ByteBuffer) buffer.flip());
}
}

View file

@ -0,0 +1,75 @@
package ms.net.event;
import java.nio.ByteBuffer;
import ms.ServerConstants;
import ms.net.IoReadEvent;
import ms.net.IoSession;
import ms.world.WorldDatabase;
import ms.system.util.ByteBufferUtils;
import ms.world.info.CountryFlag;
import ms.world.info.WorldInfo;
/**
* Handles world registry read events.
* @author Emperor
*
*/
public final class RegistryReadEvent extends IoReadEvent {
/**
* The string check.
*/
private static final String CHECK = "12x4578f5g45hrdjiofed59898";
//kratos = 666x14x88x28shhhwpwwb&h
//12x4578f5g45hrdjiofed59898
/**
* Constructs a new {@code RegistryReadEvent} {@code Object}.
* @param session The session.
* @param buffer The buffer to read.
*/
public RegistryReadEvent(IoSession session, ByteBuffer buffer) {
super(session, buffer);
}
@Override
public void read(IoSession session, ByteBuffer buffer) {
int worldId = buffer.get() & 0xFF;
if (buffer.remaining() < 2) {
queueBuffer(worldId);
return;
}
int revision = buffer.getInt();
int country = buffer.get() & 0xFF;
boolean members = buffer.get() == 1;
boolean pvp = buffer.get() == 1;
boolean quickChat = buffer.get() == 1;
boolean lootshare = buffer.get() == 1;
String activity = ByteBufferUtils.getString(buffer);
System.out.println("["+ revision + "], country = " + country + ", members = " + members + ", pvp = " + pvp + ", quickChat = " + quickChat + ", lootShare = " + lootshare + ", activity = " + activity);
for (int i = 0; i < CHECK.length(); i++) {
if ((char) buffer.get() != CHECK.charAt(i)) {
session.write(3);
return;
}
}
if (worldId >= ServerConstants.WORLD_LIMIT) {
session.write(0);
return;
}
if (WorldDatabase.isActive(worldId)) {
session.write(2);
return;
}
try {
WorldInfo info = new WorldInfo(worldId, session.getAddress(), revision, CountryFlag.values()[country], activity, members, pvp, quickChat, lootshare);
WorldDatabase.register(info).configure(session);
session.write(1);
} catch (Throwable t) {
t.printStackTrace();
session.write(3);
}
}
}

View file

@ -0,0 +1,43 @@
package ms.net.event;
import java.nio.ByteBuffer;
import ms.net.EventProducer;
import ms.net.IoSession;
import ms.net.IoWriteEvent;
import ms.net.producer.PacketEventProducer;
/**
* Handles game world registry writing events.
* @author Emperor
*
*/
public final class RegistryWriteEvent extends IoWriteEvent {
/**
* The event producer.
*/
public static final EventProducer PRODUCER = new PacketEventProducer();
/**
* Constructs a new {@code RegistryWriteEvent} {@code Object}.
* @param session The I/O session.
* @param context The writing context.
*/
public RegistryWriteEvent(IoSession session, Object context) {
super(session, context);
}
@Override
public void write(IoSession session, Object context) {
ByteBuffer buffer = ByteBuffer.allocate(1);
int opcode = (int) context;
buffer.put((byte) opcode);
buffer.flip();
if (opcode == 1) {
session.setProducer(PRODUCER);
}
session.queue(buffer);
}
}

View file

@ -0,0 +1,677 @@
package ms.net.packet;
import java.nio.ByteBuffer;
import ms.system.util.ByteBufferUtils;
/**
* Represents the buffer used for reading/writing packets.
* @author Emperor
*
*/
public class IoBuffer {
/**
* The bit masks.
*/
private static final int[] BIT_MASK = new int[32];
/**
* The packet size.
*/
private int packetSize;
/**
* The opcode.
*/
private final int opcode;
/**
* The packet header.
*/
private final PacketHeader header;
/**
* The byte buffer.
*/
private ByteBuffer buf;
/**
* The bit position.
*/
private int bitPosition = 0;
/**
* Constructs a new {@code IoBuffer} {@code Object}.
*/
public IoBuffer() {
this(-1, PacketHeader.NORMAL, ByteBuffer.allocate(2048));
}
/**
* Constructs a new {@code IoBuffer} {@code Object}.
* @param opcode The opcode.
*/
public IoBuffer(int opcode) {
this(opcode, PacketHeader.NORMAL, ByteBuffer.allocate(2048));
}
/**
* Constructs a new {@code IoBuffer} {@code Object}.
* @param opcode The opcode.
* @param header The packet header.
*/
public IoBuffer(int opcode, PacketHeader header) {
this(opcode, header, ByteBuffer.allocate((1 << 16) + 1));
}
/**
* Constructs a new {@code IoBuffer} {@code Object}.
* @param opcode The opcode.
* @param header The packet header.
* @param buf The byte buffer.
*/
public IoBuffer(int opcode, PacketHeader header, ByteBuffer buf) {
this.opcode = opcode;
this.header = header;
this.buf = buf;
}
static {
for (int i = 0; i < 32; i++) {
BIT_MASK[i] = (1 << i) - 1;
}
}
/**
*
* @return
*/
public IoBuffer clear() {
buf.clear();
bitPosition = 0;
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer put(int val) {
buf.put((byte) val);
return this;
}
/**
*
* @param datas
* @param offset
* @param len
* @return
*/
public IoBuffer putBytes(byte[] datas, int offset, int len) {
for (int i = offset; i < len; i++) {
put(datas[i]);
}
return this;
}
public final void getBytes(byte data[], int off, int len) {
for (int k = off; k < len + off; k++) {
data[k] = data[off++];
}
}
/**
*
* @param val
* @return
*/
public IoBuffer putA(int val) {
buf.put((byte) (val + 128));
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putC(int val) {
buf.put((byte) -val);
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putS(int val) {
buf.put((byte) (128 - val));
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putTri(int val) {
buf.put((byte) (val >> 16));
buf.put((byte) (val >> 8));
buf.put((byte) val);
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putShort(int val) {
buf.putShort((short) val);
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putLEShort(int val) {
buf.put((byte) val);
buf.put((byte) (val >> 8));
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putShortA(int val) {
buf.put((byte) (val >> 8));
buf.put((byte) (val + 128));
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putLEShortA(int val) {
buf.put((byte) (val + 128));
buf.put((byte) (val >> 8));
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putInt(int val) {
buf.putInt(val);
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putLEInt(int val) {
buf.put((byte) val);
buf.put((byte) (val >> 8));
buf.put((byte) (val >> 16));
buf.put((byte) (val >> 24));
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putIntA(int val) {
buf.put((byte) (val >> 8));
buf.put((byte) val);
buf.put((byte) (val >> 24));
buf.put((byte) (val >> 16));
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putIntB(int val) {
buf.put((byte) (val >> 16));
buf.put((byte) (val >> 24));
buf.put((byte) val);
buf.put((byte) (val >> 8));
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putLong(long val) {
buf.putLong(val);
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putSmart(int val) {
if (val > Byte.MAX_VALUE) {
buf.putShort((short) (val + 32768));
} else {
buf.put((byte) val);
}
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putIntSmart(int val) {
if (val > Short.MAX_VALUE) {
buf.putInt(val + 32768);
} else {
buf.putShort((short) val);
}
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putString(String val) {
buf.put(val.getBytes());
buf.put((byte) 0);
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putJagString(String val) {
buf.put((byte) 0);
buf.put(val.getBytes());
buf.put((byte) 0);
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer putJagString2(String val) {
byte[] packed = new byte[256];
int length = ByteBufferUtils.packGJString2(0, packed, val);
buf.put((byte) 0).put(packed, 0, length).put((byte) 0);
return this;
}
/**
*
* @param val
* @return
*/
public IoBuffer put(byte[] val) {
buf.put(val);
return this;
}
/**
* Puts a byte array as byte A in reverse.
* @param data The data to put.
* @param start The start index.
* @param offset The offset.
*/
public void putReverseA(byte[] data, int start, int offset) {
for (int i = offset + start; i >= start; i--) {
putA(data[i]);
}
}
/**
*
* @param numBits
* @param value
* @return
*/
public IoBuffer putBits(int numBits, int value) {
int bytePos = getBitPosition() >> 3;
int bitOffset = 8 - (getBitPosition() & 7);
bitPosition += numBits;
for (; numBits > bitOffset; bitOffset = 8) {
byte b = buf.get(bytePos);
buf.put(bytePos, b &= ~BIT_MASK[bitOffset]);
buf.put(bytePos++, b |= value >> numBits - bitOffset & BIT_MASK[bitOffset]);
numBits -= bitOffset;
}
byte b = buf.get(bytePos);
if (numBits == bitOffset) {
buf.put(bytePos, b &= ~BIT_MASK[bitOffset]);
buf.put(bytePos, b |= value & BIT_MASK[bitOffset]);
} else {
buf.put(bytePos, b &= ~(BIT_MASK[numBits] << bitOffset - numBits));
buf.put(bytePos, b |= (value & BIT_MASK[numBits]) << bitOffset - numBits);
}
return this;
}
/**
*
* @param buffer
* @return
*/
public IoBuffer put(IoBuffer buffer) {
buffer.toByteBuffer().flip();
buf.put(buffer.toByteBuffer());
return this;
}
/**
*
* @param buffer
* @return
*/
public IoBuffer putA(IoBuffer buffer) {
buffer.toByteBuffer().flip();
while (buffer.toByteBuffer().hasRemaining()) {
putA(buffer.toByteBuffer().get());
}
return this;
}
/**
*
* @param buffer
* @return
*/
public IoBuffer put(ByteBuffer buffer) {
buf.put(buffer);
return this;
}
/**
*
* @return
*/
public IoBuffer setBitAccess() {
bitPosition = buf.position() * 8;
return this;
}
/**
*
* @return
*/
public IoBuffer setByteAccess() {
buf.position((getBitPosition() + 7) / 8);
return this;
}
/**
*
* @return
*/
public int get() {
return buf.get();
}
/**
*
* @return
*/
public int getA() {
return (buf.get() & 0xFF) - 128;
}
/**
*
* @return
*/
public int getC() {
return -buf.get();
}
/**
*
* @return
*/
public int getS() {
return 128 - buf.get();
}
/**
*
* @return
*/
public int getTri() {
return ((buf.get() << 16) & 0xFF) | ((buf.get() << 8) & 0xFF) | (buf.get() & 0xFF);
}
/**
*
* @return
*/
public int getShort() {
return buf.getShort();
}
/**
*
* @return
*/
public int getLEShort() {
return (buf.get() & 0xFF) | ((buf.get() & 0xFF) << 8);
}
/**
*
* @return
*/
public int getShortA() {
return ((buf.get() & 0xFF) << 8) | (buf.get() - 128 & 0xFF);
}
/**
*
* @return
*/
public int getLEShortA() {
return (buf.get() - 128 & 0xFF) | ((buf.get() & 0xFF) << 8);
}
/**
*
* @return
*/
public int getInt() {
return buf.getInt();
}
/**
*
* @return
*/
public int getLEInt() {
return (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8)
+ ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24);
}
/**
*
* @return
*/
public int getIntA() {
return ((buf.get() & 0xFF) << 8) + (buf.get() & 0xFF)+ ((buf.get() & 0xFF) << 24) + ((buf.get() & 0xFF) << 16);
}
/**
*
* @return
*/
public int getIntB() {
return ((buf.get() & 0xFF) << 16) + ((buf.get() & 0xFF) << 24)
+ (buf.get() & 0xFF) + ((buf.get() & 0xFF) << 8);
}
/**
*
* @return
*/
public long getLongL() {
long first = getIntB();
long second = getIntB();
if (second < 0)
second = second & 0xffffffffL;
return (first << -41780448) + second;
}
/**
*
* @return
*/
public long getLong() {
return buf.getLong();
}
/**
*
* @return
*/
public int getSmart() {
int peek = buf.get(buf.position());
if (peek <= Byte.MAX_VALUE) {
return buf.get() & 0xFF;
}
return (buf.getShort() & 0xFFFF) - 32768;
}
/**
*
* @return
*/
public int getIntSmart() {
int peek = buf.getShort(buf.position());
if (peek <= Short.MAX_VALUE) {
return buf.getShort() & 0xFFFF;
}
return (buf.getInt() & 0xFFFFFFFF) - 32768;
}
/**
*
* @return
*/
public String getString() {
return ByteBufferUtils.getString(buf);
}
/**
*
* @return
*/
public String getJagString() {
buf.get();
return ByteBufferUtils.getString(buf);
}
/**
*
* @param is
* @param offset
* @param length
* @return
*/
public IoBuffer getReverseA(byte[] is, int offset, int length) {
for (int i = (offset + length - 1); i >= offset; i--) {
is[i] = (byte) (buf.get() - 128);
}
return this;
}
/**
*
* @return
*/
public ByteBuffer toByteBuffer() {
return buf;
}
/**
*
* @return
*/
public int opcode() {
return opcode;
}
/**
*
* @return
*/
public int readableBytes() {
return buf.capacity() - buf.remaining();
}
/**
*
* @return
*/
public PacketHeader getHeader() {
return header;
}
/**
*
* @return
*/
public byte[] array() {
return buf.array();
}
/**
* @return the packetSize.
*/
public int getPacketSize() {
return packetSize;
}
/**
* @param packetSize the packetSize to set.
*/
public void setPacketSize(int packetSize) {
this.packetSize = packetSize;
}
/**
* Gets the bitPosition.
* @return The bitPosition.
*/
public int getBitPosition() {
return bitPosition;
}
}

View file

@ -0,0 +1,25 @@
package ms.net.packet;
/**
* Represents the types of packet headers.
* @author Emperor
*
*/
public enum PacketHeader {
/**
* The normal packet header.
*/
NORMAL,
/**
* The byte packet header.
*/
BYTE,
/**
* The short packet header.
*/
SHORT;
}

View file

@ -0,0 +1,587 @@
package ms.net.packet;
import java.nio.ByteBuffer;
import java.util.List;
import ms.ServerConstants;
import ms.net.IoSession;
import ms.system.PunishmentStorage;
import ms.world.GameServer;
import ms.world.PlayerSession;
import ms.world.WorldDatabase;
import ms.system.communication.ClanRank;
import ms.system.communication.ClanRepository;
import ms.system.communication.CommunicationInfo;
import ms.world.info.Response;
import ms.world.info.UIDInfo;
/**
* Repository class for world packets.
* @author Emperor
*
*/
public final class WorldPacketRepository {
/**
* Sends the player registry response.
* @param server The game server.
* @param player The player session.
* @param response The registry response.
*/
public static void sendRegistryResponse(GameServer server, PlayerSession player, Response response) {
IoBuffer buffer = new IoBuffer(0, PacketHeader.BYTE);
buffer.putString(player.getUsername());
buffer.put((byte) response.opcode());
if (response == Response.MOVING_WORLD) {
long delay = ServerConstants.WORLD_SWITCH_DELAY - (System.currentTimeMillis() - player.getDisconnectionTime());
buffer.put((byte) (delay / 1000));
}
server.getSession().write(buffer);
}
/**
* Sends a message to the player.
* @param player The player.
* @param message The message to send.
*/
public static void sendPlayerMessage(PlayerSession player, String message) {
if (player == null) {
return;
}
IoBuffer buffer = new IoBuffer(2, PacketHeader.BYTE);
buffer.putString(player.getUsername());
buffer.putString(message);
player.getWorld().getSession().write(buffer);
}
public static void sendPlayerMessage(PlayerSession player, String[] messages) {
if (player == null) {
return;
}
for (String message : messages)
sendPlayerMessage(player, message);
}
/**
* Sends the contact information.
* @param player The player.
*/
public static void sendContactInformation(PlayerSession player) {
CommunicationInfo info = player.getCommunication();
IoBuffer buffer = new IoBuffer(3, PacketHeader.SHORT);
buffer.putString(player.getUsername());
buffer.put(info.getContacts().size());
for (String contact : info.getContacts().keySet()) {
buffer.putString(contact);
buffer.put(info.getRank(contact).ordinal());
buffer.put(CommunicationInfo.getWorldId(player, contact));
}
buffer.put(info.getBlocked().size());
for (String contact : info.getBlocked()) {
buffer.putString(contact);
}
if (info.getCurrentClan() == null) {
buffer.put(0);
} else {
buffer.put(1);
buffer.putString(info.getCurrentClan());
}
player.getWorld().getSession().write(buffer);
}
/**
* Sends a contact update.
* @param player The player who's contacts we're changing.
* @param contact The contact to update.
* @param block If we're updating the blocked list.
* @param remove If the contact should be removed.
* @param worldId The world id of the contact.
* @param rank The clan rank.
*/
public static void sendContactUpdate(PlayerSession player, String contact, boolean block, boolean remove, int worldId, ClanRank rank) {
IoBuffer buffer = new IoBuffer(4, PacketHeader.BYTE);
buffer.putString(player.getUsername());
buffer.putString(contact);
buffer.put((byte) (block ? 1 : 0));
if (rank != null) {
buffer.put((byte) (2 + rank.ordinal()));
} else {
buffer.put((byte) (remove ? 1 : 0));
if (!block && !remove) {
buffer.put((byte) worldId);
}
}
player.getWorld().getSession().write(buffer);
}
/**
* Sends a clan message.
* @param player The player to send the message to.
* @param p The player sending the message.
* @param message The message to send.
* @param type The message type.
*/
public static void sendMessage(PlayerSession player, PlayerSession p, int type, String message) {
IoBuffer buffer = new IoBuffer(5, PacketHeader.BYTE);
buffer.putString(player.getUsername());
buffer.putString(p.getUsername());
buffer.put((byte) type);
buffer.put((byte) p.getChatIcon());
buffer.putString(message);
player.getWorld().getSession().write(buffer);
}
/**
* Sends clan information to the server.
* @param server The server.
* @param clan The clan.
*/
public static void sendClanInformation(GameServer server, ClanRepository clan) {
IoBuffer buffer = new IoBuffer(6, PacketHeader.SHORT);
buffer.putString(clan.getOwner().getUsername());
buffer.putString(clan.getName());
int length = clan.getPlayers().size();
if (length > ClanRepository.MAX_MEMBERS) {
length = ClanRepository.MAX_MEMBERS;
}
buffer.put((byte) length);
for (int i = 0; i < length; i++) {
PlayerSession player = clan.getPlayers().get(i);
buffer.putString(player.getUsername());
buffer.put(player.getWorldId());
buffer.put((byte) clan.getRank(player).ordinal());
}
buffer.put((byte) clan.getJoinRequirement().ordinal());
buffer.put((byte) clan.getKickRequirement().ordinal());
buffer.put((byte) clan.getMessageRequirement().ordinal());
buffer.put((byte) clan.getLootRequirement().ordinal());
server.getSession().write(buffer);
}
/**
* Sends the leave clan packet.
* @param player The player leaving the clan.
*/
public static void sendLeaveClan(PlayerSession player) {
IoBuffer buffer = new IoBuffer(7, PacketHeader.BYTE);
buffer.putString(player.getUsername());
player.getWorld().getSession().write(buffer);
}
/**
* Sends the player login notification.
* @param server The server.
* @param player The player logging in.
* @param names The names.
*/
public static void notifyPlayers(GameServer server, PlayerSession player, List<String> names) {
IoBuffer buffer = new IoBuffer(8, PacketHeader.SHORT);
buffer.putString(player.getUsername());
buffer.put((byte) player.getWorldId());
buffer.put((byte) names.size());
for (String name : names) {
buffer.putString(name);
}
server.getSession().write(buffer);
}
/**
* Notifies the game server a player logged out.
* @param server The game server to notify.
* @param player The player logging out.
*/
public static void notifyLogout(GameServer server, PlayerSession player) {
IoBuffer buffer = new IoBuffer(9, PacketHeader.BYTE);
buffer.putString(player.getUsername());
server.getSession().write(buffer);
}
/**
* Sends the update countdown to the server.
* @param server The server.
* @param ticks The amount of ticks left.
*/
public static void sendUpdate(GameServer server, int ticks) {
IoBuffer buffer = new IoBuffer(10);
buffer.putInt(ticks);
server.getSession().write(buffer);
}
/**
* Sends the punishment update packet.
* @param world The world to send the packet to.
* @param key The punishment key.
* @param type The punishment type.
* @param duration The duration of the punishment.
*/
public static void sendPunishUpdate(GameServer world, String key, int type, long duration) {
IoBuffer buffer = new IoBuffer(11, PacketHeader.BYTE);
buffer.putString(key);
buffer.put((byte) type);
buffer.putLong(duration);
world.getSession().write(buffer);
}
/**
* Sends a configuration reload.
* @param world the world.
*/
public static void sendConfigReload(GameServer world) {
world.getSession().write(new IoBuffer(15, PacketHeader.BYTE));
}
/**
* Handles incoming world packets.
* @param session The I/O session.
* @param opcode The opcode.
* @param b The buffer to read from.
*/
public static void handleIncoming(IoSession session, int opcode, ByteBuffer b) {
IoBuffer buffer = new IoBuffer(opcode, PacketHeader.NORMAL, b);
GameServer server = session.getGameServer();
switch (opcode) {
case 0:
handlePlayerRegistration(server, buffer);
break;
case 1:
handlePlayerRemoval(server, buffer);
break;
case 2:
handlePunishment(server, buffer);
break;
case 3:
handleCommunicationRequest(server, buffer);
break;
case 4:
case 5:
handleContactUpdate(server, buffer, opcode == 5);
break;
case 6:
handleJoinClan(server, buffer);
break;
case 7:
handleClanRename(server, buffer);
break;
case 8:
handleClanSetting(server, buffer);
break;
case 9:
handleClanKick(server, buffer);
break;
case 10:
handleClanMessage(server, buffer);
break;
case 11:
handlePrivateMessage(server, buffer);
break;
case 12:
handleClanInfoRequest(server, buffer);
break;
case 13:
handleChatSetting(server, buffer);
break;
case 14:
handleInfoUpdate(server, buffer);
break;
default:
System.err.println("Handling incoming packet [opcode=" + opcode + ", size=" + b.limit() + "].");
}
}
/**
* Handles the info of a player update.
* @param server the server.
* @param buffer the buffer.
*/
private static void handleInfoUpdate(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
PlayerSession player = server.getPlayers().get(username);
if (player != null) {
player.setChatIcon((int) buffer.get());
}
}
/**
* Handles a player registration.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handlePlayerRegistration(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
String password = buffer.getString();
String ipAddress = buffer.getString();
String macAddress = buffer.getString();
String compName = buffer.getString();
String serial = buffer.getString();
int rights = server.getInfo().getRevision() == 498 ? 0 : buffer.getInt();
int chatIcon = server.getInfo().getRevision() == 498 ? 0 : buffer.get();
UIDInfo uid = new UIDInfo(ipAddress, compName, macAddress, serial);
PlayerSession player = new PlayerSession(username, password, new UIDInfo(ipAddress, compName, macAddress, serial));
if (WorldDatabase.isActivePlayer(username)) {
sendRegistryResponse(server, player, Response.ALREADY_ONLINE);
return;
}
player.setUid(uid);
player.setRights(rights);
player.setChatIcon(chatIcon);
if (PunishmentStorage.isSystemBanned(uid)) {
sendRegistryResponse(server, player, Response.BANNED);
return;
}
player.parse();
if (player.isBanned()) {
sendRegistryResponse(server, player, Response.ACCOUNT_DISABLED);
return;
}
if (player.getLastWorld() != server.getInfo().getWorldId() && player.hasMovedWorld()) {
sendRegistryResponse(server, player, Response.MOVING_WORLD);
return;
}
server.register(player);
}
/**
* Handles the removal of a player.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handlePlayerRemoval(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
PlayerSession session = server.getPlayers().get(username);
if (session != null) {
session.setActive(false);
PlayerSession player = server.getPlayers().remove(username);
if (player != null) {
session.remove();
}
session.setWorldId(0);
}
}
/**
* Handles a player registration.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handlePunishment(GameServer server, IoBuffer buffer) {
int type = buffer.get() & 0xFF;
String target = buffer.getString();
long duration = buffer.getLong();
String staff = buffer.getString();
PunishmentStorage.handlePunishment(staff, target, type, duration);
}
/**
* Handles the communication info request packet.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handleCommunicationRequest(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
PlayerSession player = server.getPlayers().get(username);
if (player == null) {
return;
}
sendContactInformation(player);
}
/**
* Handles a contact update packet.
* @param server The server.
* @param buffer The buffer to read from.
* @param block If the list is for blocked players.
*/
private static void handleContactUpdate(GameServer server, IoBuffer buffer, boolean block) {
String username = buffer.getString();
PlayerSession player = server.getPlayers().get(username);
if (player == null) {
return;
}
String contact = buffer.getString();
switch (buffer.get()) {
case 0:
if (block) {
player.getCommunication().block(contact);
break;
}
player.getCommunication().add(contact);
break;
case 1:
player.getCommunication().remove(contact, block);
break;
case 2:
player.getCommunication().updateClanRank(contact, ClanRank.values()[buffer.get()]);
break;
}
}
/**
* Handles a clan related packet.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handleJoinClan(GameServer server, IoBuffer buffer) {
String name = buffer.getString();
String clanName = buffer.getString();
PlayerSession player = server.getPlayers().get(name);
if (player == null || !player.isActive()) {
System.err.println("Invalid player specified in clan packet!");
return;
}
if (player.getClan() != null) {
player.getClan().leave(player, true);
return;
}
if (clanName.length() < 1) {
sendLeaveClan(player);
return;
}
ClanRepository clan = ClanRepository.get(server, clanName);
if (clan == null) {
sendPlayerMessage(player, new String[]{ "The channel you tried to join does not exist.", "Try joining the main clan named '" + ServerConstants.SERVER_NAME + "'.:clan:" });
return;
}
clan.enter(player);
}
/**
* Handles renaming a clan.
* @param server The server.
* @param buffer The buffer.
*/
private static void handleClanRename(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
String name = buffer.getString();
PlayerSession player = server.getPlayers().get(username);
if (player == null || !player.isActive()) {
return;
}
ClanRepository clan = ClanRepository.getClans().get(username);
player.getCommunication().setClanName(name);
if (clan != null) {
if (name.length() < 1) {
clan.clean(true);
} else {
clan.rename(name);
}
}
}
/**
* Handles changing clan settings packet.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handleClanSetting(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
int type = buffer.get();
ClanRank rank = type < 4 ? ClanRank.values()[buffer.get() & 0xFF] : null;
PlayerSession player = server.getPlayers().get(username);
if (player == null || !player.isActive()) {
return;
}
ClanRepository clan = ClanRepository.get(server,username);
switch (type) {
case 0:
player.getCommunication().setJoinRequirement(rank);
if (clan != null) {
clan.clean(false);
}
break;
case 1:
player.getCommunication().setMessageRequirement(rank);
break;
case 2:
player.getCommunication().setKickRequirement(rank);
if (clan != null) {
clan.update();
}
break;
case 3:
player.getCommunication().setLootRequirement(rank);
break;
}
}
/**
* Handles kicking a player from the clan.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handleClanKick(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
String playerName = buffer.getString();
PlayerSession player = server.getPlayers().get(username);
if (player == null || !player.isActive() || player.getClan() == null) {
return;
}
PlayerSession target = WorldDatabase.getPlayer(playerName);
if (target == null || !target.isActive()) {
return;
}
player.getClan().kick(player, target);
}
/**
* Handles a clan message.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handleClanMessage(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
String message = buffer.getString();
PlayerSession player = server.getPlayers().get(username);
if (player == null || !player.isActive() || player.getClan() == null) {
return;
}
player.getClan().message(player, message);
}
/**
* Handles a clan message.
* @param server The game server.
* @param buffer The buffer.
*/
private static void handlePrivateMessage(GameServer server, IoBuffer buffer) {
String username = buffer.getString();
String receiver = buffer.getString();
String message = buffer.getString();
PlayerSession player = server.getPlayers().get(username);
if (player == null || !player.isActive()) {
return;
}
player.getCommunication().sendMessage(receiver, message);
}
/**
* Handles a clan information request packet.
* @param server The server.
* @param buffer The buffer.
*/
private static void handleClanInfoRequest(GameServer server, IoBuffer buffer) {
String name = buffer.getString();
ClanRepository clan = ClanRepository.get(server, name);
if (clan == null) {
return;
}
sendClanInformation(server, clan);
}
/**
* Handles a chat setting update packet.
* @param server The server.
* @param buffer The buffer.
*/
private static void handleChatSetting(GameServer server, IoBuffer buffer) {
String name = buffer.getString();
int publicSetting = buffer.get();
int privateSetting = buffer.get();
int tradeSetting = buffer.get();
PlayerSession player = server.getPlayers().get(name);
if (player == null || !player.isActive()) {
return;
}
player.getCommunication().updateSettings(publicSetting, privateSetting, tradeSetting);
}
}

View file

@ -0,0 +1,29 @@
package ms.net.producer;
import java.nio.ByteBuffer;
import ms.net.EventProducer;
import ms.net.IoReadEvent;
import ms.net.IoSession;
import ms.net.IoWriteEvent;
import ms.net.event.HSReadEvent;
import ms.net.event.HSWriteEvent;
/**
* Produces I/O events for the handshake protocol.
* @author Emperor
*
*/
public final class HSEventProducer implements EventProducer {
@Override
public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) {
return new HSReadEvent(session, buffer);
}
@Override
public IoWriteEvent produceWriter(IoSession session, Object context) {
return new HSWriteEvent(session, context);
}
}

View file

@ -0,0 +1,29 @@
package ms.net.producer;
import java.nio.ByteBuffer;
import ms.net.EventProducer;
import ms.net.IoReadEvent;
import ms.net.IoSession;
import ms.net.IoWriteEvent;
import ms.net.event.PacketReadEvent;
import ms.net.event.PacketWriteEvent;
/**
* The packet event producer.
* @author Emperor
*
*/
public final class PacketEventProducer implements EventProducer {
@Override
public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) {
return new PacketReadEvent(session, buffer);
}
@Override
public IoWriteEvent produceWriter(IoSession session, Object context) {
return new PacketWriteEvent(session, context);
}
}

View file

@ -0,0 +1,29 @@
package ms.net.producer;
import java.nio.ByteBuffer;
import ms.net.EventProducer;
import ms.net.IoReadEvent;
import ms.net.IoSession;
import ms.net.IoWriteEvent;
import ms.net.event.RegistryReadEvent;
import ms.net.event.RegistryWriteEvent;
/**
* Handles world server registry.
* @author Emperor
*
*/
public final class RegistryEventProducer implements EventProducer {
@Override
public IoReadEvent produceReader(IoSession session, ByteBuffer buffer) {
return new RegistryReadEvent(session, buffer);
}
@Override
public IoWriteEvent produceWriter(IoSession session, Object context) {
return new RegistryWriteEvent(session, context);
}
}

View file

@ -0,0 +1,13 @@
package ms.system;
/**
* The operating systems
* @author Clayton Williams
*
*/
public enum OperatingSystem {
UNIX,
WINDOWS
}

View file

@ -0,0 +1,263 @@
package ms.system;
import ms.net.packet.WorldPacketRepository;
import ms.system.mysql.SQLManager;
import ms.world.GameServer;
import ms.world.PlayerSession;
import ms.world.WorldDatabase;
import ms.world.info.UIDInfo;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Used for storing and handling punishment data.
* @author Emperor
* @author Vexia
*
*/
public final class PunishmentStorage {
/**
* The type ids for IP, MAC and SERIAL bans.
*/
public static final int IP = 2, MAC = 3, SERIAL = 4;
/**
* Constructs a new {@code PunishmentStorage} {@code Object}.
*/
public PunishmentStorage() {
/*
* empty.
*/
}
/**
* Handles a punishment.
* @param staff The name of the staff member dealing the punishment.
* @param target The target.
* @param type The punishment type.
* @param duration The duration of the punishment (in milliseconds).
*/
public static void handlePunishment(String name, String target, int type, long duration) {
PlayerSession staff = WorldDatabase.getPlayer(name);
PlayerSession player = WorldDatabase.getPlayer(target, true);
if (player == null) {
WorldPacketRepository.sendPlayerMessage(staff, "Player " + target + " is invalid!");
return;
}
long end = Long.MAX_VALUE;
if (duration != -1l && duration != 0L) {
end = System.currentTimeMillis() + duration;
} else if (duration == 0L) {
end = 0L;
}
String key = "null";
switch (type) {
case 0: //Mute
case 1: //Ban
if (type == 1 && end == 0L) {
unban(player.getIpAddress());
unban(player.getMacAddress());
unban(player.getSerialKey());
}
if (player.isActive()) {
WorldPacketRepository.sendPunishUpdate(player.getWorld(), player.getUsername(), type, end);
}
notify(staff, type == 0 ? "mute" : "ban", target, end);
Connection connection = SQLManager.getConnection();
if (connection == null) {
return;
}
PreparedStatement statement;
try {
statement = connection.prepareStatement("UPDATE members SET " + (type == 0 ? "muteTime" : "banTime") + "='" + end + "' WHERE username ='" + target + "'");
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
SQLManager.close(connection);
return;
}
SQLManager.close(connection);
return;
case 2:
ban(player.getIpAddress(), type);
notify(staff, "IP-ban", target, end);
notifyServers(key = player.getIpAddress(), type, end);
break;
case 3:
ban(player.getMacAddress(), type);
notify(staff, "MAC-ban", target, end);
notifyServers(key = player.getMacAddress(), type, end);
break;
case 4:
ban(player.getSerialKey(), type);
notify(staff, "UID-ban", target, end);
notifyServers(key = player.getSerialKey(), type, end);
break;
case 5:
ban(player.getIpAddress(), 2);
notifyServers(key = player.getIpAddress(), 2, end);
ban(player.getMacAddress(), 3);
notifyServers(key = player.getMacAddress(), 3, end);
ban(player.getSerialKey(), 4);
notifyServers(key = player.getSerialKey(), 4, end);
notify(staff, "full ban", target, end);
return;
case 6: //Kick
if (player.isActive()) {
WorldPacketRepository.sendPunishUpdate(player.getWorld(), player.getUsername(), 6, end);
WorldPacketRepository.sendPlayerMessage(staff, "Successfully kicked player " + target + " from world " + player.getWorldId() + ".");
} else {
WorldPacketRepository.sendPlayerMessage(staff, "Player " + target + " was already inactive.");
}
break;
case 7: //Request info
WorldPacketRepository.sendPlayerMessage(staff, "[----------Player info----------]");
WorldPacketRepository.sendPlayerMessage(staff, "Name: " + player.getUsername());
WorldPacketRepository.sendPlayerMessage(staff, "IP address: " + player.getIpAddress());
WorldPacketRepository.sendPlayerMessage(staff, "MAC address: " + player.getMacAddress());
WorldPacketRepository.sendPlayerMessage(staff, "Serial key: " + player.getSerialKey());
WorldPacketRepository.sendPlayerMessage(staff, "Computer name: " + player.getComputerName());
WorldPacketRepository.sendPlayerMessage(staff, "[-------------------------------]");
return;
}
notifyServers(key, type, end);
}
/**
* Bans an address.
* @param address the address.
* @return {@code True} if banned.
*/
public static boolean ban(String address, int type) {
if (address == null || address.length() == 0 || address.equals("To be filled by O.E.M.") || address.equals("To be filled by O.E.M") || address.equals("Base Board Serial Number")) {
System.out.println("Error! Can't ban address " + address + " type = " + type + "!");
return false;
}
if (isBanned(address)) {
return false;
}
Connection connection = SQLManager.getConnection();
if (connection == null) {
return false;
}
PreparedStatement statement;
try {
statement = connection.prepareStatement("INSERT INTO punishments VALUES('" + address + "','" + type + "')");
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
SQLManager.close(connection);
return false;
}
SQLManager.close(connection);
return true;
}
/**
* Unbans an address.
* @param address the address.
* @return {@code True} if unbanned.
*/
public static boolean unban(String address) {
Connection connection = SQLManager.getConnection();
if (connection == null) {
return false;
}
PreparedStatement statement;
try {
statement = connection.prepareStatement("DELETE from punishments WHERE address ='" + address + "'");
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
SQLManager.close(connection);
return false;
}
SQLManager.close(connection);
return true;
}
/**
* Notifies the punishing player of success.
* @param staff The punishing player.
* @param type The punishment type.
* @param target The target name.
* @param end The end time stamp of the punishment.
*/
private static void notify(PlayerSession staff, String type, String target, long end) {
if (end <= System.currentTimeMillis()) {
WorldPacketRepository.sendPlayerMessage(staff, "Successfully removed punishment [type=" + type + ", player=" + target + "].");
return;
}
WorldPacketRepository.sendPlayerMessage(staff, "Successfully punished player " + target + " [type=" + type + ", duration=" + getDuration(end) + "].");
}
/**
* Notifies the game servers of a punishment update.
* @param key The punishment key.
* @param type The type.
* @param duration The duration.
*/
public static void notifyServers(String key, int type, long duration) {
for (GameServer server : WorldDatabase.getWorlds()) {
if (server != null && server.isActive()) {
WorldPacketRepository.sendPunishUpdate(server, key, type, duration);
}
}
}
/**
* Checks if the UID Info is banned.
* @param info the info.
* @return {@code True} if banned.
*/
public static boolean isSystemBanned(UIDInfo info) {
return isBanned(info.getIp()) || isBanned(info.getMac()) || isBanned(info.getSerial());
}
/**
* Checked if an address is banned.
* @param address the address.
* @param type the type.
* @return {@code True} if so.
*/
public static boolean isBanned(String address) {
Connection connection = SQLManager.getConnection();
if (connection == null) {
return false;
}
try {
ResultSet set = connection.createStatement().executeQuery("SELECT * FROM punishments WHERE address ='" + address +"'");
if (set == null || !set.next()) {
SQLManager.close(connection);
return false;
}
} catch (SQLException e) {
e.printStackTrace();
SQLManager.close(connection);
return false;
}
SQLManager.close(connection);
return true;
}
/**
* Gets the duration string representation.
* @param end The end time.
* @return The string.
*/
private static String getDuration(long end) {
String time = "indefinite time";
if (end != Long.MAX_VALUE) {
int days = (int) ((end -= System.currentTimeMillis()) / (24 * 60 * 60_000));
int hours = (int) ((end -= (days * 24 * 60 * 60_000)) / (60 * 60_000));
int minutes = (int) ((end -= (hours * (60 * 60_000))) / 60_000);
time = days + "d, " + hours + "h, " + minutes + "m";
}
return time;
}
}

View file

@ -0,0 +1,27 @@
package ms.system;
import ms.Management;
/**
* The shutdown sequence used for safely turning off the Management server.
* @author Emperor
*
*/
public final class ShutdownSequence extends Thread {
@Override
public void run() {
if (Management.active) {
shutdown();
}
}
/**
* Safely shuts down the Management server.
*/
public static void shutdown() {
System.out.println("Management server successfully shut down!");
Management.active = false;
}
}

View file

@ -0,0 +1,54 @@
package ms.system.communication;
/**
* Represents the rank of a clan member.
* @author Emperor
*/
public enum ClanRank {
NONE(-1, "Anyone"),
FRIEND(0, "Any friends"),
RECRUIT(1, "Recruit+"),
CORPORAL(2, "Corporal+"),
SERGEANT(3, "Sergeant+"),
LIEUTENANT(4, "Lieutenant+"),
CAPTAIN(5, "Captain+"),
GENERAL(6, "General+"),
OWNER(7, "Only me"),
ADMINISTRATOR(127, "No-one");
/**
* The value of the rank.
*/
private final int value;
/**
* The requirement info.
*/
private final String info;
/**
* Constructs a new {@code ClanRank} {@code Object}.
* @param value The rank value.
* @param info The requirement info.
*/
private ClanRank(int value, String info) {
this.value = value;
this.info = info;
}
/**
* Gets the value.
* @return The value.
*/
public int getValue() {
return value;
}
/**
* Gets the info.
* @return The info.
*/
public String getInfo() {
return info;
}
}

View file

@ -0,0 +1,337 @@
package ms.system.communication;
import ms.net.packet.WorldPacketRepository;
import ms.system.util.StringUtils;
import ms.world.GameServer;
import ms.world.PlayerSession;
import ms.world.WorldDatabase;
import ms.world.info.UIDInfo;
import java.util.*;
/**
* Holds clan related information.
* @author Emperor
*
*/
public final class ClanRepository {
/**
* The mapping of active clans.
*/
private static final Map<String, ClanRepository> CLANS = new HashMap<>();
/**
* The maximum amount of members to be in a clan chat.
*/
public static final int MAX_MEMBERS = 100;
/**
* The owner's details.
*/
private PlayerSession owner;
/**
* The list of players currently in the chat.
*/
private final List<PlayerSession> players = new ArrayList<>(MAX_MEMBERS);
/**
* The banned players.
*/
private final Map<String, Long> banned = new HashMap<>();
/**
* Constructs a new {@code ClanRepository} {@code Object}.
*/
private ClanRepository() {
/*
* empty.
*/
}
/**
* Gets the clan repository for the given username.
* @param ownerName The clan owner's name.
* @return The clan repository.
*/
public static ClanRepository get(GameServer server, String ownerName) {
ClanRepository clan = CLANS.get(ownerName);
if (clan != null) {
return clan;
}
PlayerSession owner = WorldDatabase.getPlayer(ownerName);
if (owner == null) {
owner = new PlayerSession(ownerName, ownerName, new UIDInfo());
owner.parse();
}
if (owner.getCommunication().getClanName().equals("")) {
return null;
}
clan = new ClanRepository();
clan.owner = owner;
CLANS.put(ownerName, clan);
return clan;
}
/**
* Enters the clan chat.
* @param player The player.
*/
public void enter(PlayerSession player) {
if (players.size() >= MAX_MEMBERS && !owner.getUsername().equals("2009scape")) {
WorldPacketRepository.sendPlayerMessage(player, "The channel you tried to join is full.:clan:");
return;
}
if (player != owner && player.getRights() != 2) {
if (isBanned(player.getUsername()) || owner.getCommunication().getBlocked().contains(player.getUsername())) {
WorldPacketRepository.sendPlayerMessage(player, "You are temporarily banned from this clan channel.:clan:");
return;
}
ClanRank rank = getRank(player);
if (rank.ordinal() < getJoinRequirement().ordinal()) {
WorldPacketRepository.sendPlayerMessage(player, "You do not have a high enough rank to join this clan channel.:clan:");
return;
}
}
if (!players.contains(player)) {
players.add(player);
}
WorldPacketRepository.sendPlayerMessage(player, "Now talking in clan channel " + owner.getCommunication().getClanName() + ".:clan:");
WorldPacketRepository.sendPlayerMessage(player, "To talk, start each line of chat with the / symbol.:clan:");
player.getCommunication().setCurrentClan(owner.getUsername());
player.setClan(this);
update();
}
/**
* Leaves the clan chat.
* @param player The player to leave.
* @param remove If the player should be removed from the list.
*/
public void leave(PlayerSession player, boolean remove) {
if (remove) {
players.remove(player);
update();
if (players.size() < 1) {
banned.clear();
}
}
WorldPacketRepository.sendPlayerMessage(player, "You have left the channel.:clan:");
player.setClan(null);
player.getCommunication().setCurrentClan(null);
if (player.isActive()) {
WorldPacketRepository.sendLeaveClan(player);
}
}
/**
* Sends a message to all players in the chat.
* @param player The player sending the message.
* @param message The message to send.
*/
public void message(PlayerSession player, String message) {
if (player != owner && player.getRights() != 2) {
ClanRank rank = getRank(player);
if (rank.ordinal() < getMessageRequirement().ordinal()) {
WorldPacketRepository.sendPlayerMessage(player, "You do not have a high enough rank to talk in this clan channel.:clan:");
return;
}
}
for (Iterator<PlayerSession> it = players.iterator(); it.hasNext();) {
PlayerSession p = it.next();
if (p != null) {
WorldPacketRepository.sendMessage(p, player, 2, message);
}
}
}
/**
* Updates the clan chat.
*/
public void update() {
for (GameServer server : WorldDatabase.getWorlds()) {
if (server != null && server.isActive()) {
WorldPacketRepository.sendClanInformation(server, this);
}
}
}
/**
* Kicks a player from the clan chat.
* @param player The player.
*/
public void kick(PlayerSession player, PlayerSession target) {
ClanRank rank = getRank(target);
if (target.getRights() == 2) {
WorldPacketRepository.sendPlayerMessage(player, "You can't kick an administrator.:clan:");
return;
}
System.out.println(rank + ", " + player.getUsername());
if (player.getRights() < 1/*!= 2 && rank.ordinal() < getKickRequirement().ordinal()*/) {
WorldPacketRepository.sendPlayerMessage(player, "You do not have a high enough rank to kick in this clan channel.:clan:");
return;
}
if (target == owner) {
WorldPacketRepository.sendPlayerMessage(player, "You can't kick the owner of this clan channel.:clan:");
return;
}
if (target == player) {
WorldPacketRepository.sendPlayerMessage(player, "You can't kick yourself.:clan:");
return;
}
for (PlayerSession p : players) {
WorldPacketRepository.sendMessage(p, player, 2, "[Attempting to kick/ban " + StringUtils.formatDisplayName(target.getUsername()) + " from this Clan Chat.]");
}
leave(target, true);
banned.put(target.getUsername(), System.currentTimeMillis() + (3_600_000));
WorldPacketRepository.sendPlayerMessage(target, "You have been kicked from the channel.:clan:");
}
/**
* Gets the rank for the given player.
* @param player The player.
* @return The rank.
*/
public ClanRank getRank(PlayerSession player) {
ClanRank rank = owner.getCommunication().getContacts().get(player.getUsername());
if (player.getRights() == 2 && player != owner) {
return ClanRank.ADMINISTRATOR;
}
if (rank == null) {
if (player == owner) {
return ClanRank.OWNER;
}
return ClanRank.NONE;
}
return rank;
}
/**
* Checks if the player is banned.
* @param username The username of the player.
* @return {@code True} if so.
*/
private boolean isBanned(String username) {
Long time = banned.get(username);
if (time == null) {
return false;
}
if (time < System.currentTimeMillis()) {
banned.remove(username);
return false;
}
return true;
}
/**
* Clears the clan chat.
* @param disable If the clan chat is getting disabled.
*/
public void clean(boolean disable) {
for (Iterator<PlayerSession> it = players.iterator(); it.hasNext();) {
PlayerSession player = it.next();
boolean remove = disable;
if (!remove) {
remove = getRank(player).ordinal() < getJoinRequirement().getValue();
}
if (remove) {
leave(player, false);
it.remove();
}
}
if (players.isEmpty()) {
banned.clear();
}
update();
}
/**
* Renames the clan chat.
* @param name The new clan name.
*/
public void rename(String name) {
owner.getCommunication().setClanName(name);
update();
}
/**
* Gets the clan name.
* @return The clan name.
*/
public String getName() {
return owner.getCommunication().getClanName();
}
/**
* Gets the owner value.
* @return The owner.
*/
public PlayerSession getOwner() {
return owner;
}
/**
* Sets the owner value.
* @param owner The owner to set.
*/
public void setOwner(PlayerSession owner) {
this.owner = owner;
}
/**
* Gets the players value.
* @return The players.
*/
public List<PlayerSession> getPlayers() {
return players;
}
/**
* Gets the clans value.
* @return The clans.
*/
public static Map<String, ClanRepository> getClans() {
return CLANS;
}
/**
* Gets the joinRequirement value.
* @return The joinRequirement.
*/
public ClanRank getJoinRequirement() {
return owner.getCommunication().getJoinRequirement();
}
/**
* Gets the messageRequirement value.
* @return The messageRequirement.
*/
public ClanRank getMessageRequirement() {
return owner.getCommunication().getMessageRequirement();
}
/**
* Gets the kickRequirement value.
* @return The kickRequirement.
*/
public ClanRank getKickRequirement() {
return owner.getCommunication().getKickRequirement();
}
/**
* Gets the lootRequirement value.
* @return The lootRequirement.
*/
public ClanRank getLootRequirement() {
return owner.getCommunication().getLootRequirement();
}
/**
* Gets the banned value.
* @return The banned.
*/
public Map<String, Long> getBanned() {
return banned;
}
}

View file

@ -0,0 +1,655 @@
package ms.system.communication;
import ms.ServerConstants;
import ms.net.packet.WorldPacketRepository;
import ms.system.util.ByteBufferUtils;
import ms.system.util.StringUtils;
import ms.world.GameServer;
import ms.world.PlayerSession;
import ms.world.WorldDatabase;
import java.nio.ByteBuffer;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
/**
* Holds communication information.
* @author Emperor
*
*/
public final class CommunicationInfo {
/**
* The maximum list size.
*/
public static final int MAX_LIST_SIZE = 200;
/**
* The clan ranks.
*/
private final Map<String, ClanRank> contacts = new HashMap<>();
/**
* The list of blocked players.
*/
private final List<String> blocked = new ArrayList<>();
/**
* The player's clan name.
*/
private String clanName = "";
/**
* The current clan this player is in.
*/
private String currentClan = "2009scape";
/**
* The rank required for joining.
*/
private ClanRank joinRequirement = ClanRank.FRIEND;
/**
* The rank required for messaging.
*/
private ClanRank messageRequirement = ClanRank.NONE;
/**
* The rank required for kicking members.
*/
private ClanRank kickRequirement = ClanRank.OWNER;
/**
* The rank required for loot-share.
*/
private ClanRank lootRequirement = ClanRank.ADMINISTRATOR;
/**
* The public chat setting.
*/
private int publicChatSetting = 0;
/**
* The private chat setting.
*/
private int privateChatSetting = 0;
/**
* The trade setting.
*/
private int tradeSetting = 0;
/**
* The player session.
*/
private PlayerSession player;
/**
* Constructs a new {@code CommunicationInfo} {@code Object}.
* @param player The player.
*/
public CommunicationInfo(PlayerSession player) {
this.setPlayer(player);
}
/**
* Called when the player logs in.
*/
public void sync() {
if (privateChatSetting != 2) {
for (GameServer server : WorldDatabase.getWorlds()) {
if (server != null && server.isActive()) {
List<String> names = new ArrayList<>();
for (PlayerSession p : server.getPlayers().values()) {
if (p.isActive() && p.getCommunication().contacts.containsKey(player.getUsername())) {
if (privateChatSetting == 0 || contacts.containsKey(p.getUsername())) {
names.add(p.getUsername());
}
}
}
WorldPacketRepository.notifyPlayers(server, player, names);
}
}
}
}
/**
* Called when the player logs out.
*/
public void clear() {
for (GameServer server : WorldDatabase.getWorlds()) {
if (server != null && server.isActive()) {
WorldPacketRepository.notifyLogout(server, player);
}
}
if (player.getClan() != null) {
player.getClan().leave(player, true);
}
}
/**
* Saves the communication info.
* @param statement The buffer.
* @throws SQLException The exception if thrown.
*/
public void save(PreparedStatement statement) throws SQLException {
String contacts = "";
String blocked = "";
for (int i = 0; i < this.blocked.size(); i++) {
blocked += (i == 0 ? "" : ",") + this.blocked.get(i);
}
int count = 0;
for (Entry<String, ClanRank> entry : this.contacts.entrySet()) {
contacts += "{" + entry.getKey() + "," + entry.getValue().ordinal() + "}" + (count == this.contacts.size() - 1 ? "" : "~");
count++;
}
statement.setString(3, contacts);
statement.setString(4, blocked);
statement.setString(5, clanName);
statement.setString(6, currentClan);
statement.setString(7, joinRequirement.ordinal() + "," + messageRequirement.ordinal() + "," + kickRequirement.ordinal() + "," + lootRequirement.ordinal());
statement.setString(8, publicChatSetting + "," + privateChatSetting + "," + tradeSetting);
}
/**
* Parses the communication info from the database.
* @param set The result set.
* @throws SQLException The exception if thrown.
*/
public void parse(ResultSet set) throws SQLException {
String contacts = set.getString("contacts");
String[] tokens;
if (contacts != null && !contacts.isEmpty()) {
String[] datas = contacts.split("~");
for (String d : datas) {
tokens = d.replace("{", "").replace("}", "").split(",");
if (tokens.length < 2) {
continue;
}
this.contacts.put(tokens[0], ClanRank.values()[Integer.parseInt(tokens[1])]);
}
}
String bl = set.getString("blocked");
if (bl != null && !bl.isEmpty()) {
tokens = bl.split(",");
for (String name : tokens) {
blocked.add(name);
}
}
clanName = set.getString("clanName");
currentClan = set.getString("currentClan");
String clanReqs = set.getString("clanReqs");
if (!clanReqs.isEmpty()) {
tokens = clanReqs.split(",");
ClanRank rank = null;
int ordinal = 0;
for (int i = 0; i < tokens.length; i++) {
ordinal = Integer.parseInt(tokens[i]);
if (ordinal < 0 || ordinal > ClanRank.values().length -1) {
continue;
}
rank = ClanRank.values()[ordinal];
switch (i) {
case 0:
joinRequirement = rank;
break;
case 1:
messageRequirement = rank;
break;
case 2:
if (ordinal < 3 || ordinal > 8) {
break;
}
kickRequirement = rank;
break;
case 3:
lootRequirement = rank;
break;
}
}
}
String chatSettings = set.getString("chatSettings");
if (!chatSettings.isEmpty()) {
tokens = chatSettings.split(",");
for (int i = 0; i < tokens.length; i++) {
switch (i) {
case 0:
publicChatSetting = Integer.parseInt(tokens[0]);
break;
case 1:
privateChatSetting = Integer.parseInt(tokens[1]);
break;
case 2:
tradeSetting = Integer.parseInt(tokens[2]);
break;
}
}
}
}
/**
* Saves the communication info.
* @param buffer The buffer.
*/
public void save(ByteBuffer buffer) {
buffer.put((byte) contacts.size());
for (String name : contacts.keySet()) {
ByteBufferUtils.putString(name, buffer);
buffer.put((byte) contacts.get(name).ordinal());
}
buffer.put((byte) blocked.size());
for (String name : blocked) {
ByteBufferUtils.putString(name, buffer);
}
ByteBufferUtils.putString(clanName, buffer);
if (currentClan != null) {
ByteBufferUtils.putString(currentClan, buffer.put((byte) 1));
} else {
buffer.put((byte) 0);
}
buffer.put((byte) joinRequirement.ordinal());
buffer.put((byte) messageRequirement.ordinal());
buffer.put((byte) kickRequirement.ordinal());
buffer.put((byte) lootRequirement.ordinal());
buffer.put((byte) publicChatSetting);
buffer.put((byte) privateChatSetting);
buffer.put((byte) tradeSetting);
}
/**
* Parses the communication info from the buffer.
* @param buffer The buffer.
*/
public void parse(ByteBuffer buffer) {
int length = buffer.get() & 0xFF;
for (int i = 0; i < length; i++) {
contacts.put(ByteBufferUtils.getString(buffer), ClanRank.values()[buffer.get() & 0xFF]);
}
length = buffer.get() & 0xFF;
for (int i = 0; i < length; i++) {
blocked.add(ByteBufferUtils.getString(buffer));
}
clanName = ByteBufferUtils.getString(buffer);
if (buffer.get() == 1) {
currentClan = ByteBufferUtils.getString(buffer);
}
joinRequirement = ClanRank.values()[buffer.get()];
messageRequirement = ClanRank.values()[buffer.get()];
kickRequirement = ClanRank.values()[buffer.get()];
lootRequirement = ClanRank.values()[buffer.get()];
publicChatSetting = buffer.get();
privateChatSetting = buffer.get();
tradeSetting = buffer.get();
}
/**
* Sends a message to the target.
* @param target The target.
* @param message The message to send.
*/
public void sendMessage(String target, String message) {
PlayerSession receiver = WorldDatabase.getPlayer(target);
if (receiver == null || !receiver.isActive()) {
WorldPacketRepository.sendPlayerMessage(player, "That player is currently offline.");
return;
}
WorldPacketRepository.sendMessage(player, receiver, 0, message);
WorldPacketRepository.sendMessage(receiver, player, 1, message);
}
/**
* Adds a contact.
* @param contact The contact to add.
*/
public void add(String contact) {
if (contacts.size() >= MAX_LIST_SIZE) {
WorldPacketRepository.sendPlayerMessage(player, "Your friend list is full.");
return;
}
if (blocked.contains(contact)) {
WorldPacketRepository.sendPlayerMessage(player, "Please remove " + StringUtils.formatDisplayName(contact) + " from your ignored list first.");
return;
}
if (contacts.containsKey(contact)) {
WorldPacketRepository.sendPlayerMessage(player, StringUtils.formatDisplayName(contact) + " is already on your friend list.");
return;
}
contacts.put(contact, ClanRank.FRIEND);
WorldPacketRepository.sendContactUpdate(player, contact, false, false, getWorldId(player, contact), null);
ClanRepository clan = ClanRepository.getClans().get(player.getUsername());
if (clan != null) {
clan.update();
}
if (privateChatSetting == 1) {
PlayerSession other = WorldDatabase.getPlayer(contact);
if (other != null && other.isActive() && other.getCommunication().getContacts().containsKey(player.getUsername())) {
WorldPacketRepository.sendContactUpdate(other, player.getUsername(), false, false, getWorldId(other, player.getUsername()), null);
}
}
}
/**
* Gets the world id for the given contact.
* @param player The player.
* @param contact The contact.
* @return The world id to display.
*/
public static int getWorldId(PlayerSession player, String contact) {
PlayerSession p = WorldDatabase.getPlayer(contact);
if (p == null || !p.isActive() || p.getCommunication().getPrivateChatSetting() == 2) {
return 0;
}
if (p.getCommunication().getBlocked().contains(player.getUsername())) {
return 0;
}
if (p.getCommunication().getPrivateChatSetting() == 1) {
if (p.getCommunication().getContacts().containsKey(player.getUsername())) {
return p.getWorldId();
}
return 0;
}
return p.getWorldId();
}
/**
* Removes a contact.
* @param contact The contact to remove.
* @param block If the contact should be removed from the block list.
*/
public void remove(String contact, boolean block) {
PlayerSession other = WorldDatabase.getPlayer(contact);
if (block) {
blocked.remove(contact);
if (other != null && other.isActive() && other.getCommunication().getContacts().containsKey(player.getUsername())) {
WorldPacketRepository.sendContactUpdate(other, player.getUsername(), false, false, getWorldId(other, player.getUsername()), null);
}
} else {
contacts.remove(contact);
ClanRepository clan = ClanRepository.getClans().get(player.getUsername());
if (clan != null) {
clan.update();
}
if (privateChatSetting == 1 && other != null && other.isActive() && other.getCommunication().getContacts().containsKey(player.getUsername())) {
WorldPacketRepository.sendContactUpdate(other, player.getUsername(), false, false, getWorldId(other, player.getUsername()), null);
}
}
WorldPacketRepository.sendContactUpdate(player, contact, block, true, 0, null);
}
/**
* Adds a blocked contact.
* @param contact The contact to block.
*/
public void block(String contact) {
if (blocked.size() >= MAX_LIST_SIZE) {
WorldPacketRepository.sendPlayerMessage(player, "Your ignore list is full.");
return;
}
if (contacts.containsKey(contact)) {
WorldPacketRepository.sendPlayerMessage(player, "Please remove " + StringUtils.formatDisplayName(contact) + " from your friends list first.");
return;
}
if (blocked.contains(contact)) {
WorldPacketRepository.sendPlayerMessage(player, StringUtils.formatDisplayName(contact) + " is already on your friend list.");
return;
}
blocked.add(contact);
WorldPacketRepository.sendContactUpdate(player, contact, true, false, 0, null);
PlayerSession other = WorldDatabase.getPlayer(contact);
if (other != null && other.isActive() && other.getCommunication().getContacts().containsKey(player.getUsername())) {
WorldPacketRepository.sendContactUpdate(other, player.getUsername(), false, false, 0, null);
}
}
/**
* Updates the clan rank of a certain contact.
* @param contact The contact.
* @param clanRank The clan rank to set.
*/
public void updateClanRank(String contact, ClanRank clanRank) {
if (!contacts.containsKey(contact)) {
System.err.println("Could not find contact " + contact + " to update clan rank!");
return;
}
contacts.put(contact, clanRank);
ClanRepository clan = ClanRepository.getClans().get(player.getUsername());
if (clan != null) {
clan.update();
}
WorldPacketRepository.sendContactUpdate(player, contact, false, false, 0, clanRank);
}
/**
* Updates the settings.
* @param publicSetting The public chat setting.
* @param privateSetting The private chat setting.
* @param tradeSetting The trade setting.
*/
public void updateSettings(int publicSetting, int privateSetting, int tradeSetting) {
this.publicChatSetting = publicSetting;
this.tradeSetting = tradeSetting;
if (this.privateChatSetting != privateSetting) {
updatePrivateSetting(privateSetting);
}
}
/**
* Updates the private chat setting.
* @param privateSetting The private chat setting.
*/
private void updatePrivateSetting(int privateSetting) {
this.privateChatSetting = privateSetting;
for (GameServer server : WorldDatabase.getWorlds()) {
if (server != null && server.isActive()) {
if (privateSetting == 2) {
WorldPacketRepository.notifyLogout(server, player);
continue;
}
for (PlayerSession p : server.getPlayers().values()) {
if (p.isActive() && p.getCommunication().contacts.containsKey(player.getUsername())) {
WorldPacketRepository.sendContactUpdate(p, player.getUsername(), false, false, getWorldId(p, player.getUsername()), null);
}
}
}
}
}
/**
* Gets the contacts value.
* @return The contacts.
*/
public Map<String, ClanRank> getContacts() {
return contacts;
}
/**
* Gets the clan rank for the given contact.
* @param contact The contact.
* @return The rank.
*/
public ClanRank getRank(String contact) {
for (String name : ServerConstants.ADMINISTRATORS) {
if (contact.equals(name)) {
return ClanRank.ADMINISTRATOR;
}
}
return contacts.get(contact);
}
/**
* Gets the blocked value.
* @return The blocked.
*/
public List<String> getBlocked() {
return blocked;
}
/**
* Gets the clanName value.
* @return The clanName.
*/
public String getClanName() {
return clanName;
}
/**
* Sets the clanName value.
* @param clanName The clanName to set.
*/
public void setClanName(String clanName) {
this.clanName = clanName;
}
/**
* Gets the currentClan value.
* @return The currentClan.
*/
public String getCurrentClan() {
return currentClan;
}
/**
* Sets the currentClan value.
* @param currentClan The currentClan to set.
*/
public void setCurrentClan(String currentClan) {
this.currentClan = currentClan;
}
/**
* Gets the player value.
* @return The player.
*/
public PlayerSession getPlayer() {
return player;
}
/**
* Sets the player value.
* @param player The player to set.
*/
public void setPlayer(PlayerSession player) {
this.player = player;
}
/**
* Gets the joinRequirement value.
* @return The joinRequirement.
*/
public ClanRank getJoinRequirement() {
return joinRequirement;
}
/**
* Sets the joinRequirement value.
* @param joinRequirement The joinRequirement to set.
*/
public void setJoinRequirement(ClanRank joinRequirement) {
this.joinRequirement = joinRequirement;
}
/**
* Gets the messageRequirement value.
* @return The messageRequirement.
*/
public ClanRank getMessageRequirement() {
return messageRequirement;
}
/**
* Sets the messageRequirement value.
* @param messageRequirement The messageRequirement to set.
*/
public void setMessageRequirement(ClanRank messageRequirement) {
this.messageRequirement = messageRequirement;
}
/**
* Gets the kickRequirement value.
* @return The kickRequirement.
*/
public ClanRank getKickRequirement() {
return kickRequirement;
}
/**
* Sets the kickRequirement value.
* @param kickRequirement The kickRequirement to set.
*/
public void setKickRequirement(ClanRank kickRequirement) {
this.kickRequirement = kickRequirement;
}
/**
* Gets the lootRequirement value.
* @return The lootRequirement.
*/
public ClanRank getLootRequirement() {
return lootRequirement;
}
/**
* Sets the lootRequirement value.
* @param lootRequirement The lootRequirement to set.
*/
public void setLootRequirement(ClanRank lootRequirement) {
this.lootRequirement = lootRequirement;
}
/**
* Gets the publicChatSetting value.
* @return The publicChatSetting.
*/
public int getPublicChatSetting() {
return publicChatSetting;
}
/**
* Sets the publicChatSetting value.
* @param publicChatSetting The publicChatSetting to set.
*/
public void setPublicChatSetting(int publicChatSetting) {
this.publicChatSetting = publicChatSetting;
}
/**
* Gets the privateChatSetting value.
* @return The privateChatSetting.
*/
public int getPrivateChatSetting() {
return privateChatSetting;
}
/**
* Sets the privateChatSetting value.
* @param privateChatSetting The privateChatSetting to set.
*/
public void setPrivateChatSetting(int privateChatSetting) {
this.privateChatSetting = privateChatSetting;
}
/**
* Gets the tradeSetting value.
* @return The tradeSetting.
*/
public int getTradeSetting() {
return tradeSetting;
}
/**
* Sets the tradeSetting value.
* @param tradeSetting The tradeSetting to set.
*/
public void setTradeSetting(int tradeSetting) {
this.tradeSetting = tradeSetting;
}
}

View file

@ -0,0 +1,121 @@
package ms.system.mysql;
/**
* Represents a column used in an SQL table.
* @author Emperor
*
*/
public final class SQLColumn {
/**
* The name.
*/
private final String name;
/**
* The data type.
*/
private final Class<?> type;
/**
* If this column should never be updated in the database.
*/
private final boolean neverUpdate;
/**
* The value.
*/
private Object value;
/**
* If the column value changed.
*/
private boolean changed;
/**
* Constructs a new {@code SQLColumn} {@code Object}.
* @param name The column name.
* @param type The data type.
*/
public SQLColumn(String name, Class<?> type) {
this(name, type, false);
}
/**
* Constructs a new {@code SQLColumn} {@code Object}.
* @param name The column name.
* @param type The data type.
* @param neverUpdate If this column should never be updated in the database.
*/
public SQLColumn(String name, Class<?> type, boolean neverUpdate) {
this.name = name;
this.type = type;
this.neverUpdate = neverUpdate;
}
/**
* Gets the name.
* @return The name.
*/
public String getName() {
return name;
}
/**
* Updates the value.
* @param value The value.
*/
public void updateValue(Object value) {
this.changed = value != this.value;
this.value = value;
}
/**
* Gets the value.
* @return The value.
*/
public Object getValue() {
return value;
}
/**
* Sets the value.
* @param value The value to set.
*/
public void setValue(Object value) {
this.value = value;
this.changed = false;
}
/**
* Gets the changed.
* @return The changed.
*/
public boolean isChanged() {
return changed;
}
/**
* Sets the changed.
* @param changed The changed to set.
*/
public void setChanged(boolean changed) {
this.changed = changed;
}
/**
* Gets the type.
* @return The type.
*/
public Class<?> getType() {
return type;
}
/**
* Gets the neverUpdate.
* @return The neverUpdate.
*/
public boolean isNeverUpdate() {
return neverUpdate;
}
}

View file

@ -0,0 +1,202 @@
package ms.system.mysql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Handles an SQL entry.
* @author Emperor
* @param <T> The entry type.
*/
public abstract class SQLEntryHandler<T> {
/**
* The connection.
*/
protected Connection connection;
/**
* The entry.
*/
protected T entry;
/**
* The result set.
*/
protected ResultSet result;
/**
* The table name.
*/
protected String table;
/**
* The column name.
*/
private String column;
/**
* The value to use.
*/
protected String value;
/**
* Constructs a new {@code SQLEntryHandler} {@code Object}.
* @param entry The entry.
* @param table The table name.
* @param column column name.
* @param value The column value.
*/
public SQLEntryHandler(T entry, String table, String column, String value) {
this.entry = entry;
this.table = table;
this.column = column;
this.value = value;
}
/**
* Reads the SQL entry.
* @param entry The entry.
*/
public static void read(SQLEntryHandler<?> entry) {
entry.connection = entry.getConnection();
if (entry.connection == null) {
//System.err.println("Could not read SQL data: connection is null!");
return;
}
try {
entry.read();
if (entry.result == null || !entry.result.next()) {
entry.create();
} else {
entry.parse();
}
} catch (SQLException e) {
e.printStackTrace();
}
SQLManager.close(entry.connection);
entry.connection = null;
}
/**
* Writes the entry data on the SQL database.
* @param entry The entry.
*/
public static void write(SQLEntryHandler<?> entry) {
write(entry, entry.getConnection(), true);
}
/**
* Reads the SQL entry.
* @param entry The entry.
*/
public static void write(SQLEntryHandler<?> entry, Connection connection, boolean commit) {
if (connection == null) {
//System.err.println("Could not write SQL data: connection is null!");
return;
}
entry.connection = connection;
try {
if (!commit) {
entry.connection.setAutoCommit(false);
}
entry.save();
} catch (SQLException e) {
e.printStackTrace();
}
if (commit) {
SQLManager.close(entry.connection);
}
entry.connection = null;
}
/**
* Reads the table from the SQL database.
* @throws SQLException When an SQL exception occurs.
*/
public void read() throws SQLException {
PreparedStatement statement = connection.prepareStatement("SELECT " + getReadSelection() + " FROM " + table + " WHERE " + column + " = ?");
statement.setString(1, value);
result = statement.executeQuery();
}
/**
* Gets the result.
* @return the result.
*/
public ResultSet getResult() {
return result;
}
/**
* Gets the selection.
* @return The selection.
*/
public String getReadSelection() {
return "*";
}
/**
* Gets the writing statement.
* @param create If we are creating a new row.
* @param columns The columns to update.
*/
public PreparedStatement getWritingStatement(boolean create, String...columns) {
PreparedStatement statement = null;
try {
StringBuilder sb = new StringBuilder();
if (create) {
sb.append("INSERT INTO ").append(table).append(" (").append(column);
for (String name : columns) {
sb.append(",").append(name);
}
sb.append(") VALUES (?");
for (int i = 0; i < columns.length; i++) {
sb.append(",?");
}
sb.append(")");
} else {
sb.append("UPDATE ").append(table).append(" SET ");
for (int i = 0; i < columns.length; i++) {
sb.append(columns[i]).append("=?");
if (i < columns.length - 1) {
sb.append(",");
}
}
sb.append(" WHERE ").append(column).append("=?");
}
statement = connection.prepareStatement(sb.toString());
statement.setString(create ? 1 : columns.length + 1, value);
} catch (SQLException e) {
e.printStackTrace();
}
return statement;
}
/**
* Parses the entry.
* @throws SQLException When an SQL exception occurs.
*/
public abstract void parse() throws SQLException;
/**
* Creates a new row in the database.
* @throws SQLException When an SQL exception occurs.
*/
public abstract void create() throws SQLException;
/**
* Saves the entry.
* @throws SQLException When an SQL exception occurs.
*/
public abstract void save() throws SQLException;
/**
* Gets the connection.
* @return The connection.
*/
public abstract Connection getConnection();
}

View file

@ -0,0 +1,100 @@
package ms.system.mysql;
import ms.ServerConstants;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
/**
* Manages the sql connections.
* @author Vexia
*
*/
public final class SQLManager {
/**
* If the sql manager is locally hosted.
*/
public static final boolean LOCAL = true;
/**
* The database URL.
*/
public static final String DATABASE_URL = (LOCAL ? "127.0.0.1" : "keldagrim.org") + ":3306/" + (SQLManager.LOCAL ? "global" : ServerConstants.DATABASE_NAMES[1]);
/**
* The username of the user.
*/
private static final String USERNAME = (LOCAL ? "root" : "keldagr1_user");
/**
* The password of the user.
*/
private static final String PASSWORD = (LOCAL ? "" : "2jf4wkz$");
/**
* IF the sql manager is initialized.
*/
private static boolean initialized;
/**
* Constructs a new {@code SQLManager} {@code Object}
*/
public SQLManager() {
/**
* empty.
*/
}
/**
* Initializes the sql manager.
*/
public static void init() {
initialized = true;
WorldListSQLHandler.clearWorldList();
}
/**
* Gets a connection from the pool.
* @return The connection.
*/
public static Connection getConnection() {
try {
return DriverManager.getConnection("jdbc:mysql://" + DATABASE_URL + "?useTimezone=true&serverTimezone=UTC", USERNAME, PASSWORD);
} catch (SQLException e) {
System.out.println("Error: Mysql error message=" + e.getMessage() + ".");
}
return null;
}
/**
* Releases the connection so it's available for usage.
* @param connection The connection.
*/
public static void close(Connection connection) {
try {
connection.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
/**
* Gets the initialized.
* @return the initialized
*/
public static boolean isInitialized() {
return initialized;
}
/**
* Sets the bainitialized.
* @param initialized the initialized to set.
*/
public static void setInitialized(boolean initialized) {
SQLManager.initialized = initialized;
}
}

View file

@ -0,0 +1,63 @@
package ms.system.mysql;
import java.util.ArrayList;
import java.util.List;
/**
* Represents an SQL table.
* @author Emperor
*
*/
public final class SQLTable {
/**
* The columns.
*/
private final SQLColumn[] columns;
/**
* Constructs a new {@code SQLTable} {@code Object}.
* @param columns The columns.
*/
public SQLTable(SQLColumn...columns) {
this.columns = columns;
}
/**
* Gets the column for the given name.
* @param name The column name.
* @return The column.
*/
public SQLColumn getColumn(String name) {
for (SQLColumn column : columns) {
if (column.getName().equals(name)) {
return column;
}
}
return null;
}
/**
* Gets the changed columns.
* @return The columns.
*/
public List<SQLColumn> getChanged() {
List<SQLColumn> updated = new ArrayList<>();
for (int i = 0; i < columns.length; i++) {
SQLColumn column = columns[i];
if (column.isChanged()) {
updated.add(column);
}
}
return updated;
}
/**
* Gets the columns.
* @return The columns.
*/
public SQLColumn[] getColumns() {
return columns;
}
}

View file

@ -0,0 +1,80 @@
package ms.system.mysql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import ms.world.GameServer;
import ms.world.info.WorldInfo;
/**
* Handles the world list SQL database.
* @author Emperor
*
*/
public final class WorldListSQLHandler extends SQLEntryHandler<GameServer> {
/**
* The table for this sql entry.
*/
private static final String TABLE = "worlds";
/**
* Constructs a new {@code WorldListSQLHandler} {@code Object}.
* @param entry The game server entry.
*/
public WorldListSQLHandler(GameServer entry) {
super(entry, TABLE, "world", "" + entry.getInfo().getWorldId());
}
/**
* Clears the world list.
*/
public static void clearWorldList() {
try {
Connection connection = SQLManager.getConnection();
PreparedStatement statement = connection.prepareStatement("DELETE FROM " + TABLE);
statement.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
@Override
public void parse() throws SQLException {
}
@Override
public void create() throws SQLException {
PreparedStatement statement = connection.prepareStatement("INSERT "+ table + "(world, ip, players, country, member, revision) VALUES('" + value + "', '" + entry.getInfo().getAddress() + "', '" + entry.getPlayerAmount() + "', '" + entry.getInfo().getCountry().getId() + "', '" + (entry.getInfo().isMembers() ? 1 : 0) + "', '" + entry.getInfo().getRevision() + "')");
statement.executeUpdate();
SQLManager.close(statement.getConnection());
}
@Override
public void save() throws SQLException {
super.read();
if (result == null || !result.next()) {
create();
return;
}
int players = entry.getPlayerAmount();
WorldInfo info = entry.getInfo();
if (!entry.isActive()) {
players = -1;
}
if (players <= 0) {
PreparedStatement statement = connection.prepareStatement("UPDATE members SET online='0' WHERE lastWorld='" + value + "'");
statement.executeUpdate();
}
PreparedStatement statement = connection.prepareStatement("UPDATE " + table + " SET players='" + players + "', ip='" + info.getAddress() + "', country='" + info.getCountry().getId() + "', member='" + (info.isMembers() ? 1 : 0) + "', revision='" + info.getRevision() + "' WHERE world='" + value + "'");
statement.executeUpdate();
SQLManager.close(statement.getConnection());
}
@Override
public Connection getConnection() {
return SQLManager.getConnection();
}
}

View file

@ -0,0 +1,153 @@
package ms.system.util;
import java.nio.ByteBuffer;
/**
* Holds utility methods for reading/writing a byte buffer.
* @author Emperor
*
*/
public final class ByteBufferUtils {
/**
* Gets a string from the byte buffer.
* @param buffer The byte buffer.
* @return The string.
*/
public static String getString(ByteBuffer buffer) {
StringBuilder sb = new StringBuilder();
byte b;
while ((b = buffer.get()) != 0) {
sb.append((char) b);
}
return sb.toString();
}
/**
* Puts a string on the byte buffer.
* @param s The string to put.
* @param buffer The byte buffer.
*/
public static void putString(String s, ByteBuffer buffer) {
buffer.put(s.getBytes()).put((byte) 0);
}
/**
* Gets a string from the byte buffer.
* @param s The string.
* @param buffer The byte buffer.
* @return The string.
*/
public static ByteBuffer putGJ2String(String s, ByteBuffer buffer) {
byte[] packed = new byte[256];
int length = packGJString2(0, packed, s);
return buffer.put((byte) 0).put(packed, 0, length).put((byte) 0);
}
/**
* Decodes the XTEA encryption.
* @param keys The keys.
* @param start The start index.
* @param end The end index.
* @param buffer The byte buffer.
*/
public static void decodeXTEA(int[] keys, int start, int end, ByteBuffer buffer) {
int l = buffer.position();
buffer.position(start);
int length = (end - start) / 8;
for (int i = 0; i < length; i++) {
int firstInt = buffer.getInt();
int secondInt = buffer.getInt();
int sum = 0xc6ef3720;
int delta = 0x9e3779b9;
for (int j = 32; j-- > 0;) {
secondInt -= keys[(sum & 0x1c84) >>> 11] + sum ^ (firstInt >>> 5 ^ firstInt << 4) + firstInt;
sum -= delta;
firstInt -= (secondInt >>> 5 ^ secondInt << 4) + secondInt ^ keys[sum & 3] + sum;
}
buffer.position(buffer.position() - 8);
buffer.putInt(firstInt);
buffer.putInt(secondInt);
}
buffer.position(l);
}
/**
* Converts a String to an Integer?
*
* @param position
* The position.
* @param buffer
* The buffer used.
* @param string
* The String to convert.
* @return The Integer.
*/
public static int packGJString2(int position, byte[] buffer, String string) {
int length = string.length();
int offset = position;
for (int i = 0; length > i; i++) {
int character = string.charAt(i);
if (character > 127) {
if (character > 2047) {
buffer[offset++] = (byte) ((character | 919275) >> 12);
buffer[offset++] = (byte) (128 | ((character >> 6) & 63));
buffer[offset++] = (byte) (128 | (character & 63));
} else {
buffer[offset++] = (byte) ((character | 12309) >> 6);
buffer[offset++] = (byte) (128 | (character & 63));
}
} else
buffer[offset++] = (byte) character;
}
return offset - position;
}
/**
* Gets a tri-byte from the buffer.
* @param buffer The buffer.
* @return The value.
*/
public static int getTriByte(ByteBuffer buffer) {
return ((buffer.get() & 0xFF) << 16) + ((buffer.get() & 0xFF) << 8) + (buffer.get() & 0xFF);
}
/**
* Gets a smart from the buffer.
* @param buffer The buffer.
* @return The value.
*/
public static int getSmart(ByteBuffer buffer) {
int peek = buffer.get() & 0xFF;
if (peek <= Byte.MAX_VALUE) {
return peek;
}
return ((peek << 8) | (buffer.get() & 0xFF)) - 32768;
}
/**
* Gets a smart from the buffer.
* @param buffer The buffer.
* @return The value.
*/
public static int getBigSmart(ByteBuffer buffer) {
int value = 0;
int current = getSmart(buffer);
while (current == 32767) {
current = getSmart(buffer);
value += 32767;
}
value += current;
return value;
}
/**
* Constructs a new {@code ByteBufferUtils} {@code Object}.
*/
private ByteBufferUtils() {
/*
* empty.
*/
}
}

View file

@ -0,0 +1,52 @@
package ms.system.util;
/**
* Represents a command.
* @author Emperor
*
*/
public abstract class Command {
/**
* The command name.
*/
private final String name;
/**
* The command info.
*/
private final String info;
/**
* Constructs a new {@code Command} {@code Object}.
* @param name The command name.
* @param info The command info.
*/
public Command(String name, String info) {
this.name = name;
this.info = info;
}
/**
* Runs the command.
* @param args The arguments.
*/
public abstract void run(String...args);
/**
* Gets the name value.
* @return The name.
*/
public String getName() {
return name;
}
/**
* Gets the info value.
* @return The info.
*/
public String getInfo() {
return info;
}
}

View file

@ -0,0 +1,815 @@
package ms.system.util;
import java.io.UnsupportedEncodingException;
import java.security.SecureRandom;
/**
* Represents the class of simple encryption functions.
* @author Unknown
*/
public class EncryptionManager {
// Define the BCrypt workload to use when generating password hashes. 10-31 is a valid value.
private static int workload = 12;
/**
* Singleton instance.
*/
private static final EncryptionManager INSTANCE = new EncryptionManager();
/**
* Constructs a new {@code EncryptionManager} {@code Object}.
*/
private EncryptionManager() {
//empty.
}
/**
* This method can be used to generate a string representing an account password
* suitable for storing in a database. It will be an OpenBSD-style crypt(3) formatted
* hash string of length=60
* The bcrypt workload is specified in the above static variable, a value from 10 to 31.
* A workload of 12 is a very reasonable safe default as of 2013.
* This automatically handles secure 128-bit salt generation and storage within the hash.
* @param password_plaintext The account's plaintext password as provided during account creation,
* or when changing an account's password.
* @return String - a string of length 60 that is the bcrypt hashed password in crypt(3) format.
*/
public String hashPassword(String password_plaintext) {
String salt = BCrypt.gensalt(workload);
String hashed_password = BCrypt.hashpw(password_plaintext, salt);
return(hashed_password);
}
/**
* This method can be used to verify a computed hash from a plaintext (e.g. during a login
* request) with that of a stored hash from a database. The password hash from the database
* must be passed as the second variable.
* @param password_plaintext The account's plaintext password, as provided during a login request
* @param stored_hash The account's stored password hash, retrieved from the authorization database
* @return boolean - true if the password matches the password of the stored hash, false otherwise
*/
public boolean checkPassword(String password_plaintext, String stored_hash) {
boolean password_verified = false;
if(null == stored_hash || !stored_hash.startsWith("$2a$"))
throw new java.lang.IllegalArgumentException("Invalid hash provided for comparison");
password_verified = BCrypt.checkpw(password_plaintext, stored_hash);
return(password_verified);
}
// Copyright (c) 2006 Damien Miller <djm@mindrot.org>
//
// Permission to use, copy, modify, and distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
// MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
// ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
/**
* Gets the instance value.
* @return The instance.
*/
public static EncryptionManager getInstance() {
return INSTANCE;
}
/**
* BCrypt implements OpenBSD-style Blowfish password hashing using
* the scheme described in "A Future-Adaptable Password Scheme" by
* Niels Provos and David Mazieres.
* <p>
* This password hashing system tries to thwart off-line password
* cracking using a computationally-intensive hashing algorithm,
* based on Bruce Schneier's Blowfish cipher. The work factor of
* the algorithm is parameterised, so it can be increased as
* computers get faster.
* <p>
* Usage is really simple. To hash a password for the first time,
* call the hashpw method with a random salt, like this:
* <p>
* <code>
* String pw_hash = BCrypt.hashpw(plain_password, BCrypt.gensalt()); <br />
* </code>
* <p>
* To check whether a plaintext password matches one that has been
* hashed previously, use the checkpw method:
* <p>
* <code>
* if (BCrypt.checkpw(candidate_password, stored_hash))<br />
* &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("It matches");<br />
* else<br />
* &nbsp;&nbsp;&nbsp;&nbsp;System.out.println("It does not match");<br />
* </code>
* <p>
* The gensalt() method takes an optional parameter (log_rounds)
* that determines the computational complexity of the hashing:
* <p>
* <code>
* String strong_salt = BCrypt.gensalt(10)<br />
* String stronger_salt = BCrypt.gensalt(12)<br />
* </code>
* <p>
* The amount of work increases exponentially (2**log_rounds), so
* each increment is twice as much work. The default log_rounds is
* 10, and the valid range is 4 to 31.
*
* @author Damien Miller
* @version 0.2
*/
public static class BCrypt {
// BCrypt parameters
private static final int GENSALT_DEFAULT_LOG2_ROUNDS = 10;
private static final int BCRYPT_SALT_LEN = 16;
// Blowfish parameters
private static final int BLOWFISH_NUM_ROUNDS = 16;
// Initial contents of key schedule
private static final int P_orig[] = {
0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344,
0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89,
0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917,
0x9216d5d9, 0x8979fb1b
};
private static final int S_orig[] = {
0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7,
0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99,
0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e,
0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee,
0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef,
0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e,
0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440,
0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce,
0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e,
0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677,
0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032,
0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88,
0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e,
0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0,
0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98,
0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88,
0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6,
0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d,
0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7,
0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba,
0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f,
0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09,
0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb,
0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279,
0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab,
0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82,
0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573,
0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0,
0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790,
0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8,
0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0,
0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7,
0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad,
0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1,
0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9,
0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477,
0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49,
0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af,
0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5,
0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41,
0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400,
0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915,
0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a,
0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623,
0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266,
0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e,
0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6,
0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e,
0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1,
0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8,
0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff,
0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701,
0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7,
0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331,
0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf,
0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e,
0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87,
0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2,
0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16,
0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b,
0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509,
0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3,
0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f,
0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4,
0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960,
0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28,
0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802,
0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510,
0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf,
0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e,
0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50,
0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8,
0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281,
0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696,
0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128,
0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0,
0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0,
0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250,
0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3,
0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00,
0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061,
0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e,
0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735,
0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9,
0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340,
0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7,
0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934,
0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068,
0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840,
0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45,
0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a,
0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb,
0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6,
0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42,
0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2,
0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb,
0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b,
0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33,
0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3,
0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc,
0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564,
0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b,
0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922,
0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728,
0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e,
0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37,
0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804,
0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b,
0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb,
0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d,
0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350,
0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9,
0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe,
0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d,
0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f,
0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61,
0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9,
0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2,
0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e,
0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633,
0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169,
0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52,
0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5,
0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62,
0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76,
0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24,
0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4,
0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c,
0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0,
0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b,
0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe,
0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4,
0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8,
0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304,
0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22,
0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6,
0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9,
0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593,
0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51,
0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c,
0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b,
0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c,
0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd,
0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319,
0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb,
0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991,
0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32,
0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166,
0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae,
0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5,
0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47,
0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d,
0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84,
0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8,
0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd,
0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7,
0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38,
0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c,
0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525,
0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442,
0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964,
0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8,
0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d,
0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299,
0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02,
0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614,
0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a,
0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b,
0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0,
0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e,
0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9,
0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6
};
// bcrypt IV: "OrpheanBeholderScryDoubt"
static private final int bf_crypt_ciphertext[] = {
0x4f727068, 0x65616e42, 0x65686f6c,
0x64657253, 0x63727944, 0x6f756274
};
// Table for Base64 encoding
static private final char base64_code[] = {
'.', '/', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J',
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V',
'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5',
'6', '7', '8', '9'
};
// Table for Base64 decoding
static private final byte index_64[] = {
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
-1, -1, -1, -1, -1, -1, 0, 1, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63, -1, -1,
-1, -1, -1, -1, -1, 2, 3, 4, 5, 6,
7, 8, 9, 10, 11, 12, 13, 14, 15, 16,
17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27,
-1, -1, -1, -1, -1, -1, 28, 29, 30,
31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
41, 42, 43, 44, 45, 46, 47, 48, 49, 50,
51, 52, 53, -1, -1, -1, -1, -1
};
// Expanded Blowfish key
private int P[];
private int S[];
/**
* Encode a byte array using bcrypt's slightly-modified base64
* encoding scheme. Note that this is *not* compatible with
* the standard MIME-base64 encoding.
*
* @param d the byte array to encode
* @param len the number of bytes to encode
* @return base64-encoded string
* @exception IllegalArgumentException if the length is invalid
*/
private static String encode_base64(byte d[], int len)
throws IllegalArgumentException {
int off = 0;
StringBuffer rs = new StringBuffer();
int c1, c2;
if (len <= 0 || len > d.length)
throw new IllegalArgumentException ("Invalid len");
while (off < len) {
c1 = d[off++] & 0xff;
rs.append(base64_code[(c1 >> 2) & 0x3f]);
c1 = (c1 & 0x03) << 4;
if (off >= len) {
rs.append(base64_code[c1 & 0x3f]);
break;
}
c2 = d[off++] & 0xff;
c1 |= (c2 >> 4) & 0x0f;
rs.append(base64_code[c1 & 0x3f]);
c1 = (c2 & 0x0f) << 2;
if (off >= len) {
rs.append(base64_code[c1 & 0x3f]);
break;
}
c2 = d[off++] & 0xff;
c1 |= (c2 >> 6) & 0x03;
rs.append(base64_code[c1 & 0x3f]);
rs.append(base64_code[c2 & 0x3f]);
}
return rs.toString();
}
/**
* Look up the 3 bits base64-encoded by the specified character,
* range-checking againt conversion table
* @param x the base64-encoded value
* @return the decoded value of x
*/
private static byte char64(char x) {
if ((int)x < 0 || (int)x > index_64.length)
return -1;
return index_64[(int)x];
}
/**
* Decode a string encoded using bcrypt's base64 scheme to a
* byte array. Note that this is *not* compatible with
* the standard MIME-base64 encoding.
* @param s the string to decode
* @param maxolen the maximum number of bytes to decode
* @return an array containing the decoded bytes
* @throws IllegalArgumentException if maxolen is invalid
*/
private static byte[] decode_base64(String s, int maxolen)
throws IllegalArgumentException {
StringBuffer rs = new StringBuffer();
int off = 0, slen = s.length(), olen = 0;
byte ret[];
byte c1, c2, c3, c4, o;
if (maxolen <= 0)
throw new IllegalArgumentException ("Invalid maxolen");
while (off < slen - 1 && olen < maxolen) {
c1 = char64(s.charAt(off++));
c2 = char64(s.charAt(off++));
if (c1 == -1 || c2 == -1)
break;
o = (byte)(c1 << 2);
o |= (c2 & 0x30) >> 4;
rs.append((char)o);
if (++olen >= maxolen || off >= slen)
break;
c3 = char64(s.charAt(off++));
if (c3 == -1)
break;
o = (byte)((c2 & 0x0f) << 4);
o |= (c3 & 0x3c) >> 2;
rs.append((char)o);
if (++olen >= maxolen || off >= slen)
break;
c4 = char64(s.charAt(off++));
o = (byte)((c3 & 0x03) << 6);
o |= c4;
rs.append((char)o);
++olen;
}
ret = new byte[olen];
for (off = 0; off < olen; off++)
ret[off] = (byte)rs.charAt(off);
return ret;
}
/**
* Blowfish encipher a single 64-bit block encoded as
* two 32-bit halves
* @param lr an array containing the two 32-bit half blocks
* @param off the position in the array of the blocks
*/
private final void encipher(int lr[], int off) {
int i, n, l = lr[off], r = lr[off + 1];
l ^= P[0];
for (i = 0; i <= BLOWFISH_NUM_ROUNDS - 2;) {
// Feistel substitution on left word
n = S[(l >> 24) & 0xff];
n += S[0x100 | ((l >> 16) & 0xff)];
n ^= S[0x200 | ((l >> 8) & 0xff)];
n += S[0x300 | (l & 0xff)];
r ^= n ^ P[++i];
// Feistel substitution on right word
n = S[(r >> 24) & 0xff];
n += S[0x100 | ((r >> 16) & 0xff)];
n ^= S[0x200 | ((r >> 8) & 0xff)];
n += S[0x300 | (r & 0xff)];
l ^= n ^ P[++i];
}
lr[off] = r ^ P[BLOWFISH_NUM_ROUNDS + 1];
lr[off + 1] = l;
}
/**
* Cycically extract a word of key material
* @param data the string to extract the data from
* @param offp a "pointer" (as a one-entry array) to the
* current offset into data
* @return the next word of material from data
*/
private static int streamtoword(byte data[], int offp[]) {
int i;
int word = 0;
int off = offp[0];
for (i = 0; i < 4; i++) {
word = (word << 8) | (data[off] & 0xff);
off = (off + 1) % data.length;
}
offp[0] = off;
return word;
}
/**
* Initialise the Blowfish key schedule
*/
private void init_key() {
P = (int[])P_orig.clone();
S = (int[])S_orig.clone();
}
/**
* Key the Blowfish cipher
* @param key an array containing the key
*/
private void key(byte key[]) {
int i;
int koffp[] = { 0 };
int lr[] = { 0, 0 };
int plen = P.length, slen = S.length;
for (i = 0; i < plen; i++)
P[i] = P[i] ^ streamtoword(key, koffp);
for (i = 0; i < plen; i += 2) {
encipher(lr, 0);
P[i] = lr[0];
P[i + 1] = lr[1];
}
for (i = 0; i < slen; i += 2) {
encipher(lr, 0);
S[i] = lr[0];
S[i + 1] = lr[1];
}
}
/**
* Perform the "enhanced key schedule" step described by
* Provos and Mazieres in "A Future-Adaptable Password Scheme"
* http://www.openbsd.org/papers/bcrypt-paper.ps
* @param data salt information
* @param key password information
*/
private void ekskey(byte data[], byte key[]) {
int i;
int koffp[] = { 0 }, doffp[] = { 0 };
int lr[] = { 0, 0 };
int plen = P.length, slen = S.length;
for (i = 0; i < plen; i++)
P[i] = P[i] ^ streamtoword(key, koffp);
for (i = 0; i < plen; i += 2) {
lr[0] ^= streamtoword(data, doffp);
lr[1] ^= streamtoword(data, doffp);
encipher(lr, 0);
P[i] = lr[0];
P[i + 1] = lr[1];
}
for (i = 0; i < slen; i += 2) {
lr[0] ^= streamtoword(data, doffp);
lr[1] ^= streamtoword(data, doffp);
encipher(lr, 0);
S[i] = lr[0];
S[i + 1] = lr[1];
}
}
/**
* Perform the central password hashing step in the
* bcrypt scheme
* @param password the password to hash
* @param salt the binary salt to hash with the password
* @param log_rounds the binary logarithm of the number
* of rounds of hashing to apply
* @return an array containing the binary hashed password
*/
private byte[] crypt_raw(byte password[], byte salt[], int log_rounds) {
int rounds, i, j;
int cdata[] = (int[])bf_crypt_ciphertext.clone();
int clen = cdata.length;
byte ret[];
if (log_rounds < 4 || log_rounds > 31)
throw new IllegalArgumentException ("Bad number of rounds");
rounds = 1 << log_rounds;
if (salt.length != BCRYPT_SALT_LEN)
throw new IllegalArgumentException ("Bad salt length");
init_key();
ekskey(salt, password);
for (i = 0; i < rounds; i++) {
key(password);
key(salt);
}
for (i = 0; i < 64; i++) {
for (j = 0; j < (clen >> 1); j++)
encipher(cdata, j << 1);
}
ret = new byte[clen * 4];
for (i = 0, j = 0; i < clen; i++) {
ret[j++] = (byte)((cdata[i] >> 24) & 0xff);
ret[j++] = (byte)((cdata[i] >> 16) & 0xff);
ret[j++] = (byte)((cdata[i] >> 8) & 0xff);
ret[j++] = (byte)(cdata[i] & 0xff);
}
return ret;
}
/**
* Hash a password using the OpenBSD bcrypt scheme
* @param password the password to hash
* @param salt the salt to hash with (perhaps generated
* using BCrypt.gensalt)
* @return the hashed password
*/
public static String hashpw(String password, String salt) {
BCrypt B;
String real_salt;
byte passwordb[], saltb[], hashed[];
char minor = (char)0;
int rounds, off = 0;
StringBuffer rs = new StringBuffer();
if (salt.charAt(0) != '$' || salt.charAt(1) != '2')
throw new IllegalArgumentException ("Invalid salt version");
if (salt.charAt(2) == '$')
off = 3;
else {
minor = salt.charAt(2);
if (minor != 'a' || salt.charAt(3) != '$')
throw new IllegalArgumentException ("Invalid salt revision");
off = 4;
}
// Extract number of rounds
if (salt.charAt(off + 2) > '$')
throw new IllegalArgumentException ("Missing salt rounds");
rounds = Integer.parseInt(salt.substring(off, off + 2));
real_salt = salt.substring(off + 3, off + 25);
try {
passwordb = (password + (minor >= 'a' ? "\000" : "")).getBytes("UTF-8");
} catch (UnsupportedEncodingException uee) {
throw new AssertionError("UTF-8 is not supported");
}
saltb = decode_base64(real_salt, BCRYPT_SALT_LEN);
B = new BCrypt();
hashed = B.crypt_raw(passwordb, saltb, rounds);
rs.append("$2");
if (minor >= 'a')
rs.append(minor);
rs.append("$");
if (rounds < 10)
rs.append("0");
rs.append(Integer.toString(rounds));
rs.append("$");
rs.append(encode_base64(saltb, saltb.length));
rs.append(encode_base64(hashed,
bf_crypt_ciphertext.length * 4 - 1));
return rs.toString();
}
/**
* Generate a salt for use with the BCrypt.hashpw() method
* @param log_rounds the log2 of the number of rounds of
* hashing to apply - the work factor therefore increases as
* 2**log_rounds.
* @param random an instance of SecureRandom to use
* @return an encoded salt value
*/
public static String gensalt(int log_rounds, SecureRandom random) {
StringBuffer rs = new StringBuffer();
byte rnd[] = new byte[BCRYPT_SALT_LEN];
random.nextBytes(rnd);
rs.append("$2a$");
if (log_rounds < 10)
rs.append("0");
rs.append(Integer.toString(log_rounds));
rs.append("$");
rs.append(encode_base64(rnd, rnd.length));
return rs.toString();
}
/**
* Generate a salt for use with the BCrypt.hashpw() method
* @param log_rounds the log2 of the number of rounds of
* hashing to apply - the work factor therefore increases as
* 2**log_rounds.
* @return an encoded salt value
*/
public static String gensalt(int log_rounds) {
return gensalt(log_rounds, new SecureRandom());
}
/**
* Generate a salt for use with the BCrypt.hashpw() method,
* selecting a reasonable default for the number of hashing
* rounds to apply
* @return an encoded salt value
*/
public static String gensalt() {
return gensalt(GENSALT_DEFAULT_LOG2_ROUNDS);
}
/**
* Check that a plaintext password matches a previously hashed
* one
* @param plaintext the plaintext password to verify
* @param hashed the previously-hashed password
* @return true if the passwords match, false otherwise
*/
public static boolean checkpw(String plaintext, String hashed) {
return (hashed.compareTo(hashpw(plaintext, hashed)) == 0);
}
}
}

View file

@ -0,0 +1,19 @@
package ms.system.util;
/**
* Handles generating a player database.
* @author Emperor
*
*/
public final class PlayerDatabaseGen {
/**
* The main method.
* @param args The arguments.
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
}
}

View file

@ -0,0 +1,650 @@
package ms.system.util;
import java.text.DecimalFormat;
import ms.net.packet.IoBuffer;
/**
* The string utils.
* @author Emperor
*
*/
public final class StringUtils {
/**
* The valid characters to be used in names/messages/...
*/
public static final char[] VALID_CHARS = {
'_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q',
'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
};
/**
* Character mapping.
*/
public static char[] mapping = {
'\n',
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', ' ' };
/**
* The an int array241.
*/
public static int[] anIntArray241 = { 215, 203, 83, 158, 104, 101, 93, 84,
107, 103, 109, 95, 94, 98, 89, 86, 70, 41, 32, 27, 24, 23, -1, -2,
26, -3, -4, 31, 30, -5, -6, -7, 37, 38, 36, -8, -9, -10, 40, -11,
-12, 55, 48, 46, 47, -13, -14, -15, 52, 51, -16, -17, 54, -18, -19,
63, 60, 59, -20, -21, 62, -22, -23, 67, 66, -24, -25, 69, -26, -27,
199, 132, 80, 77, 76, -28, -29, 79, -30, -31, 87, 85, -32, -33,
-34, -35, -36, 197, -37, 91, -38, 134, -39, -40, -41, 97, -42, -43,
133, 106, -44, 117, -45, -46, 139, -47, -48, 110, -49, -50, 114,
113, -51, -52, 116, -53, -54, 135, 138, 136, 129, 125, 124, -55,
-56, 130, 128, -57, -58, -59, 183, -60, -61, -62, -63, -64, 148,
-65, -66, 153, 149, 145, 144, -67, -68, 147, -69, -70, -71, 152,
154, -72, -73, -74, 157, 171, -75, -76, 207, 184, 174, 167, 166,
165, -77, -78, -79, 172, 170, -80, -81, -82, 178, -83, 177, 182,
-84, -85, 187, 181, -86, -87, -88, -89, 206, 221, -90, 189, -91,
198, 254, 262, 195, 196, -92, -93, -94, -95, -96, 252, 255, 250,
-97, 211, 209, -98, -99, 212, -100, 213, -101, -102, -103, 224,
-104, 232, 227, 220, 226, -105, -106, 246, 236, -107, 243, -108,
-109, 231, 237, 235, -110, -111, 239, 238, -112, -113, -114, -115,
-116, 241, -117, 244, -118, -119, 248, -120, 249, -121, -122, -123,
253, -124, -125, -126, -127, 259, 258, -128, -129, 261, -130, -131,
390, 327, 296, 281, 274, 271, 270, -132, -133, 273, -134, -135,
278, 277, -136, -137, 280, -138, -139, 289, 286, 285, -140, -141,
288, -142, -143, 293, 292, -144, -145, 295, -146, -147, 312, 305,
302, 301, -148, -149, 304, -150, -151, 309, 308, -152, -153, 311,
-154, -155, 320, 317, 316, -156, -157, 319, -158, -159, 324, 323,
-160, -161, 326, -162, -163, 359, 344, 337, 334, 333, -164, -165,
336, -166, -167, 341, 340, -168, -169, 343, -170, -171, 352, 349,
348, -172, -173, 351, -174, -175, 356, 355, -176, -177, 358, -178,
-179, 375, 368, 365, 364, -180, -181, 367, -182, -183, 372, 371,
-184, -185, 374, -186, -187, 383, 380, 379, -188, -189, 382, -190,
-191, 387, 386, -192, -193, 389, -194, -195, 454, 423, 408, 401,
398, 397, -196, -197, 400, -198, -199, 405, 404, -200, -201, 407,
-202, -203, 416, 413, 412, -204, -205, 415, -206, -207, 420, 419,
-208, -209, 422, -210, -211, 439, 432, 429, 428, -212, -213, 431,
-214, -215, 436, 435, -216, -217, 438, -218, -219, 447, 444, 443,
-220, -221, 446, -222, -223, 451, 450, -224, -225, 453, -226, -227,
486, 471, 464, 461, 460, -228, -229, 463, -230, -231, 468, 467,
-232, -233, 470, -234, -235, 479, 476, 475, -236, -237, 478, -238,
-239, 483, 482, -240, -241, 485, -242, -243, 499, 495, 492, 491,
-244, -245, 494, -246, -247, 497, -248, 502, -249, 506, 503, -250,
-251, 505, -252, -253, 508, -254, 510, -255, -256, 0 };
/**
* Constructs a new {@code StringUtils.java} {@code Object}.
*/
public StringUtils() {
/**
* empty.
*/
}
/**
* Method used to get the formatted number as a string from the integer inputed.
* @param amount the ammount.
* @return the string value.
*/
public static String getFormattedNumber(int amount) {
return new DecimalFormat("#,###,##0").format(amount).toString();
}
/**
* Checks if the string contains an invalid character.
* @param nameThe string.
* @return {@code True} if so.
*/
public static boolean containsInvalidCharacter(String name) {
for (char c : name.toCharArray()) {
boolean pass = false;
for (char vc : VALID_CHARS)
if (vc == c) {
pass = true;
break;
}
if (!pass)
return true;
}
return false;
}
/**
* If a word starts with a e i o u h for grammar = a + n.
* @param word The word.
* @return If the a should have +n {@code true}.
*/
public static boolean isPlusN(String word) {
if (word == null)
return false;
String s = word.toLowerCase();
return s.charAt(0) == 'a' || s.charAt(0) == 'e' || s.charAt(0) == 'i'
|| s.charAt(0) == 'o' || s.charAt(0) == 'u'
|| (s.charAt(0) == 'h' && s.length() > 1 && s.charAt(1) != 'e');
}
/**
* Method used to get the player name as a long.
* @param s the string.
* @return the long.
*/
public static long getPlayerNameLong(String s) {
long l = 0L;
for(int i = 0; i < s.length() && i < 12; i++) {
char c = s.charAt(i);
l *= 37L;
if(c >= 'A' && c <= 'Z') l += (1 + c) - 65;
else if(c >= 'a' && c <= 'z') l += (1 + c) - 97;
else if(c >= '0' && c <= '9') l += (27 + c) - 48;
}
while(l % 37L == 0L && l != 0L) l /= 37L;
return l;
}
/**
* Method used to convert the string to a long.
* @param s the string.
* @return the long.
*/
public static long convertStringToLong(String s) {
if (s.length() > 20) {
throw new IllegalArgumentException("String is too long: " + s);
}
long out = 0L;
for (int i = 0; i < s.length(); ++i) {
long m = reducedMapping(s.codePointAt(i));
if (m == -1) {
throw new IllegalArgumentException("Unmapped Character in String: " + s);
}
m <<= ((9 - i) * 6) + 4;
out |= m;
}
return out;
}
/**
* Formats the string as display name.
* @param name The string to format.
* @return The formatted name.
*/
public static String formatDisplayName(String name) {
name = name.replaceAll("_", " ");
name = name.toLowerCase();
StringBuilder newName = new StringBuilder();
boolean wasSpace = true;
for (int i = 0; i < name.length(); i++) {
if (wasSpace) {
newName.append((new String() + name.charAt(i)).toUpperCase());
wasSpace = false;
} else {
newName.append(name.charAt(i));
}
if (name.charAt(i) == ' ') {
wasSpace = true;
}
}
return newName.toString();
}
/**
* Gets the byte for the character.
* @param c The character.
* @return The byte.
*/
private static final byte getByte(char c) {
byte charByte;
if (c > 0 && c < '\200' || c >= '\240' && c <= '\377')
charByte = (byte) c;
else if (c != '\u20AC') {
if (c != '\u201A') {
if (c != '\u0192') {
if (c == '\u201E')
charByte = -124;
else if (c != '\u2026') {
if (c != '\u2020') {
if (c == '\u2021')
charByte = -121;
else if (c == '\u02C6')
charByte = -120;
else if (c == '\u2030')
charByte = -119;
else if (c == '\u0160')
charByte = -118;
else if (c == '\u2039')
charByte = -117;
else if (c == '\u0152')
charByte = -116;
else if (c != '\u017D') {
if (c == '\u2018')
charByte = -111;
else if (c != '\u2019') {
if (c != '\u201C') {
if (c == '\u201D')
charByte = -108;
else if (c != '\u2022') {
if (c == '\u2013')
charByte = -106;
else if (c == '\u2014')
charByte = -105;
else if (c == '\u02DC')
charByte = -104;
else if (c == '\u2122')
charByte = -103;
else if (c != '\u0161') {
if (c == '\u203A')
charByte = -101;
else if (c != '\u0153') {
if (c == '\u017E')
charByte = -98;
else if (c != '\u0178')
charByte = 63;
else
charByte = -97;
} else
charByte = -100;
} else
charByte = -102;
} else
charByte = -107;
} else
charByte = -109;
} else
charByte = -110;
} else
charByte = -114;
} else
charByte = -122;
} else
charByte = -123;
} else
charByte = -125;
} else
charByte = -126;
} else
charByte = -128;
return charByte;
}
/**
* Gets the double value of this string
* @param s The string.
* @return The double value.
*/
public static double getDouble(String s) {
s = s.replaceAll(", ", "").replaceAll(",", "");
StringBuilder sb = new StringBuilder();
char c;
boolean foundStart = false;
for (int i = 0; i < s.length(); i++) {
c = s.charAt(i);
if (Character.isDigit(c) || c == '-' || c == '.') {
sb.append(c);
foundStart = true;
} else if (foundStart)
break;
}
try {
double amount = Double.parseDouble(sb.toString());
return amount;
} catch (NumberFormatException e) {
return 0.0;
}
}
/**
* Gets the hash for the string.
* @param str The string.
* @return The hash.
*/
public static final int getNameHash(String str) {
str = str.toLowerCase();
int hash = 0;
for (int index = 0; index < str.length(); index++)
hash = getByte(str.charAt(index)) + ((hash << 5) - hash);
return hash;
}
/**
* Gets the string value of this string (all html/... removed).
* @param s The string.
* @return The string value.
*/
public static String getString(String s) {
String string = s.replaceAll("\\<.*?>", "").replaceAll("&#160;", "")
.replaceAll("Discontinued Item:", "");
return string;
}
/**
* Characters used to convert a String to a Long.
*/
public static char[] validChars = {
'_', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i',
'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's',
't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2',
'3', '4', '5', '6', '7', '8', '9'
};
public static int[] anIntArray233 = {
0, 1024, 2048, 3072, 4096, 5120,
6144, 8192, 9216, 12288, 10240, 11264, 16384, 18432, 17408, 20480,
21504, 22528, 23552, 24576, 25600, 26624, 27648, 28672, 29696,
30720, 31744, 32768, 33792, 34816, 35840, 36864, 536870912,
16777216, 37888, 65536, 38912, 131072, 196608, 33554432, 524288,
1048576, 1572864, 262144, 67108864, 4194304, 134217728, 327680,
8388608, 2097152, 12582912, 13631488, 14680064, 15728640,
100663296, 101187584, 101711872, 101974016, 102760448, 102236160,
40960, 393216, 229376, 117440512, 104857600, 109051904, 201326592,
205520896, 209715200, 213909504, 106954752, 218103808, 226492416,
234881024, 222298112, 224395264, 268435456, 272629760, 276824064,
285212672, 289406976, 223346688, 293601280, 301989888, 318767104,
297795584, 298844160, 310378496, 102498304, 335544320, 299892736,
300941312, 301006848, 300974080, 39936, 301465600, 49152,
1073741824, 369098752, 402653184, 1342177280, 1610612736,
469762048, 1476395008, -2147483648, -1879048192, 352321536,
1543503872, -2013265920, -1610612736, -1342177280, -1073741824,
-1543503872, 356515840, -1476395008, -805306368, -536870912,
-268435456, 1577058304, -134217728, 360710144, -67108864,
364904448, 51200, 57344, 52224, 301203456, 53248, 54272, 55296,
56320, 301072384, 301073408, 301074432, 301075456, 301076480,
301077504, 301078528, 301079552, 301080576, 301081600, 301082624,
301083648, 301084672, 301085696, 301086720, 301087744, 301088768,
301089792, 301090816, 301091840, 301092864, 301093888, 301094912,
301095936, 301096960, 301097984, 301099008, 301100032, 301101056,
301102080, 301103104, 301104128, 301105152, 301106176, 301107200,
301108224, 301109248, 301110272, 301111296, 301112320, 301113344,
301114368, 301115392, 301116416, 301117440, 301118464, 301119488,
301120512, 301121536, 301122560, 301123584, 301124608, 301125632,
301126656, 301127680, 301128704, 301129728, 301130752, 301131776,
301132800, 301133824, 301134848, 301135872, 301136896, 301137920,
301138944, 301139968, 301140992, 301142016, 301143040, 301144064,
301145088, 301146112, 301147136, 301148160, 301149184, 301150208,
301151232, 301152256, 301153280, 301154304, 301155328, 301156352,
301157376, 301158400, 301159424, 301160448, 301161472, 301162496,
301163520, 301164544, 301165568, 301166592, 301167616, 301168640,
301169664, 301170688, 301171712, 301172736, 301173760, 301174784,
301175808, 301176832, 301177856, 301178880, 301179904, 301180928,
301181952, 301182976, 301184000, 301185024, 301186048, 301187072,
301188096, 301189120, 301190144, 301191168, 301193216, 301195264,
301194240, 301197312, 301198336, 301199360, 301201408, 301202432
};
public static byte[] aByteArray235 = {
22, 22, 22, 22, 22, 22, 21, 22, 22,
20, 22, 22, 22, 21, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 3, 8, 22, 16, 22, 16, 17, 7, 13, 13, 13,
16, 7, 10, 6, 16, 10, 11, 12, 12, 12, 12, 13, 13, 14, 14, 11, 14,
19, 15, 17, 8, 11, 9, 10, 10, 10, 10, 11, 10, 9, 7, 12, 11, 10, 10,
9, 10, 10, 12, 10, 9, 8, 12, 12, 9, 14, 8, 12, 17, 16, 17, 22, 13,
21, 4, 7, 6, 5, 3, 6, 6, 5, 4, 10, 7, 5, 6, 4, 4, 6, 10, 5, 4, 4,
5, 7, 6, 10, 6, 10, 22, 19, 22, 14, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22,
22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 21, 22, 21, 22, 22, 22, 21,
22, 22
};
/**
* Encrypt a string for the client.
*/
public static int encryptPlayerChat(byte[] is, int i_25_, int i_26_, int i_27_, byte[] is_28_) {
try {
i_27_ += i_25_;
int i_29_ = 0;
int i_30_ = i_26_ << -2116795453;
for (; i_27_ > i_25_; i_25_++) {
int i_31_ = 0xff & is_28_[i_25_];
int i_32_ = anIntArray233[i_31_];
int i_33_ = aByteArray235[i_31_];
int i_34_ = i_30_ >> -1445887805;
int i_35_ = i_30_ & 0x7;
i_29_ &= (-i_35_ >> 473515839);
i_30_ += i_33_;
int i_36_ = ((-1 + (i_35_ - -i_33_)) >> -1430991229) + i_34_;
i_35_ += 24;
is[i_34_] = (byte) (i_29_ = (i_29_ | (i_32_ >>> i_35_)));
if ((i_36_ ^ 0xffffffff) < (i_34_ ^ 0xffffffff)) {
i_34_++;
i_35_ -= 8;
is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_);
if (i_36_ > i_34_) {
i_34_++;
i_35_ -= 8;
is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_);
if (i_36_ > i_34_) {
i_35_ -= 8;
i_34_++;
is[i_34_] = (byte) (i_29_ = i_32_ >>> i_35_);
if (i_34_ < i_36_) {
i_35_ -= 8;
i_34_++;
is[i_34_] = (byte) (i_29_ = i_32_ << -i_35_);
}
}
}
}
}
return -i_26_ + ((7 + i_30_) >> -662855293);
} catch (RuntimeException runtimeexception) {
}
return 0;
}
/**
* Gets an integer value from a string.
* @param s the string.
* @return The value;
*/
public static int getValue(String s) {
s = s.replaceAll(", ", "").replaceAll(",", "");
StringBuilder sb = new StringBuilder();
char c;
boolean foundStart = false;
for (int i = 0; i < s.length(); i++) {
c = s.charAt(i);
if (Character.isDigit(c) || c == '-') {
sb.append(c);
foundStart = true;
} else if (foundStart)
break;
}
try {
int amount = Integer.parseInt(sb.toString());
return amount;
} catch (NumberFormatException e) {
return 0;
}
}
/**
* Checks if the account name is valid.
* @param name The account name.
* @return {@code True} if the account name is invalid.
*/
public static boolean invalidAccountName(String name) {
return name.length() < 2 || name.length() > 12 || name.startsWith("_")|| name.endsWith("_") || name.contains("__") || containsInvalidCharacter(name);
}
/**
* Converts a long to a string.
* @param l The long.
* @return The string.
*/
public static String longToString(long l) {
int i = 0;
char ac[] = new char[32];
while (l != 0L) {
long l1 = l;
l /= 37L;
ac[11 - i++] = VALID_CHARS[(int) (l1 - l * 37L)];
}
return new String(ac, 12 - i, i);
}
/**
* Packs a GJ2-String.
* @param position The position to start.
* @param buffer The byte-array.
* @param str The string.
* @return The size of the string.
*/
public static final int packGJString2(int position, byte[] buffer,
String str) {
int length = str.length();
int offset = position;
for (int index = 0; length > index; index++) {
int character = str.charAt(index);
if (character > 127) {
if (character > 2047) {
buffer[offset++] = (byte) ((character | 919275) >> 12);
buffer[offset++] = (byte) (128 | ((character >> 6) & 63));
buffer[offset++] = (byte) (128 | (character & 63));
} else {
buffer[offset++] = (byte) ((character | 12309) >> 6);
buffer[offset++] = (byte) (128 | (character & 63));
}
} else
buffer[offset++] = (byte) character;
}
return offset - position;
}
/**
* Method used to...
* @param x idk,
* @return the idk.
*/
public static long reducedMapping(int x) {
long out = -1;
if (x >= 97 && x <= 122)
out = x - 96;
else if (x >= 65 && x <= 90)
out = x - 37;
else if (x >= 48 && x <= 57)
out = x - +5;
else if (x == 32)
out = 63L;
return out;
}
/**
* Converts a string to a long.
* @param s The string.
* @return The long.
*/
public static long stringToLong(String s) {
long l = 0L;
for (int i = 0; i < s.length() && i < 12; i++) {
char c = s.charAt(i);
l *= 37L;
if (c >= 'A' && c <= 'Z')
l += (1 + c) - 65;
else if (c >= 'a' && c <= 'z')
l += (1 + c) - 97;
else if (c >= '0' && c <= '9')
l += (27 + c) - 48;
}
while (l % 37L == 0L && l != 0L)
l /= 37L;
return l;
}
/**
* Decrypt player chat.
*
* @param buffer
* The buffer
* @param totalChars
* The total chars
* @return The string
*/
public static String decryptPlayerChat(IoBuffer buffer, int totalChars) {
try {
if (totalChars == 0)
return "";
int charsDecoded = 0;
int i_4_ = 0;
String s = "";
for (;;) {
byte i_7_ = (byte) buffer.get();
if (i_7_ >= 0)
i_4_++;
else
i_4_ = anIntArray241[i_4_];
int i_8_;
if ((i_8_ = anIntArray241[i_4_]) < 0) {
s += (char) (byte) (i_8_ ^ 0xffffffff);
if (totalChars <= ++charsDecoded)
break;
i_4_ = 0;
}
if (((i_7_ & 0x40) ^ 0xffffffff) != -1)
i_4_ = anIntArray241[i_4_];
else
i_4_++;
if ((i_8_ = anIntArray241[i_4_]) < 0) {
s += (char) (byte) (i_8_ ^ 0xffffffff);
if (++charsDecoded >= totalChars)
break;
i_4_ = 0;
}
if ((0x20 & i_7_) == 0)
i_4_++;
else
i_4_ = anIntArray241[i_4_];
if ((i_8_ = anIntArray241[i_4_]) < 0) {
s += (char) (byte) (i_8_ ^ 0xffffffff);
if (totalChars <= ++charsDecoded)
break;
i_4_ = 0;
}
if (((0x10 & i_7_) ^ 0xffffffff) == -1)
i_4_++;
else
i_4_ = anIntArray241[i_4_];
if ((i_8_ = anIntArray241[i_4_]) < 0) {
s += (char) (byte) (i_8_ ^ 0xffffffff);
if (totalChars <= ++charsDecoded)
break;
i_4_ = 0;
}
if (((0x8 & i_7_) ^ 0xffffffff) != -1)
i_4_ = anIntArray241[i_4_];
else
i_4_++;
if ((i_8_ = anIntArray241[i_4_]) < 0) {
s += (char) (byte) (i_8_ ^ 0xffffffff);
if (++charsDecoded >= totalChars)
break;
i_4_ = 0;
}
if ((0x4 & i_7_) == 0)
i_4_++;
else
i_4_ = anIntArray241[i_4_];
if ((i_8_ = anIntArray241[i_4_]) < 0) {
s += (char) (byte) (i_8_ ^ 0xffffffff);
if (totalChars <= ++charsDecoded)
break;
i_4_ = 0;
}
if (((i_7_ & 0x2) ^ 0xffffffff) != -1)
i_4_ = anIntArray241[i_4_];
else
i_4_++;
if ((i_8_ = anIntArray241[i_4_]) < 0) {
s += (char) (byte) (i_8_ ^ 0xffffffff);
if (totalChars <= ++charsDecoded)
break;
i_4_ = 0;
}
if (((i_7_ & 0x1) ^ 0xffffffff) != -1)
i_4_ = anIntArray241[i_4_];
else
i_4_++;
if ((i_8_ = anIntArray241[i_4_]) < 0) {
s += (char) (byte) (i_8_ ^ 0xffffffff);
if (++charsDecoded >= totalChars)
break;
i_4_ = 0;
}
}
return s;
} catch (RuntimeException runtimeexception) {
runtimeexception.printStackTrace();
}
return "";
}
}

View file

@ -0,0 +1,55 @@
package ms.system.util;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
/**
* A class holding methods to execute tasks.
* @author Emperor
*
*/
public final class TaskExecutor {
/**
* The executor to use.
*/
private static final ScheduledExecutorService EXECUTOR = Executors.newSingleThreadScheduledExecutor();
/**
* The SQL task executor.
*/
private static final ScheduledExecutorService SQL_EXECUTOR = Executors.newSingleThreadScheduledExecutor();
/**
* Constructs a new {@code TaskExecutor} {@code Object}.
*/
private TaskExecutor() {
/*
* empty.
*/
}
/**
* Executes an SQL handling task.
* @param task The task.
*/
public static void executeSQL(Runnable task) {
SQL_EXECUTOR.execute(task);
}
/**
* Executes the task.
* @param task The task to execute.
*/
public static void execute(Runnable task) {
EXECUTOR.execute(task);
}
/**
* Gets the executor.
* @return The executor.
*/
public static ScheduledExecutorService getExecutor() {
return EXECUTOR;
}
}

View file

@ -0,0 +1,124 @@
package ms.world;
import ms.net.IoSession;
import ms.net.packet.WorldPacketRepository;
import ms.system.mysql.SQLEntryHandler;
import ms.system.mysql.WorldListSQLHandler;
import ms.system.util.TaskExecutor;
import ms.world.info.Response;
import ms.world.info.WorldInfo;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
/**
* Represents a game world.
* @author Emperor
*
*/
public final class GameServer {
/**
* The world info.
*/
private final WorldInfo info;
/**
* The I/O session.
*/
private IoSession session;
/**
* The players active on the game server.
*/
private final Map<String, PlayerSession> players = new HashMap<>();
/**
* The scheduled future for the updating task.
*/
private ScheduledFuture<?> future;
/**
* constructs a new {@code GameServer} {@code Object}.
* @param info The world info.
*/
public GameServer(WorldInfo info) {
this.info = info;
}
/**
* Configures the game server.
* @param session The I/O session.
*/
public void configure(IoSession session) {
this.session = session;
session.setGameServer(this);
future = TaskExecutor.getExecutor().scheduleAtFixedRate(() -> {
SQLEntryHandler.write(new WorldListSQLHandler(GameServer.this));
if (!isActive()) {
future.cancel(false);
future = null;
}
}, 0, 10, TimeUnit.SECONDS);
}
/**
* Registers a player.
* @param player The player.
*/
public void register(PlayerSession player) {
players.put(player.getUsername(), player);
player.setWorld(this);
player.setActive(true);
player.setWorldId(info.getWorldId());
player.configure();
WorldPacketRepository.sendRegistryResponse(this, player, Response.SUCCESSFUL);
player.getCommunication().sync();
}
/**
* Checks if the game server is active.
* @return {@code True} if so.
*/
public boolean isActive() {
return session.isActive();
}
/**
* @return the playerAmount
*/
public int getPlayerAmount() {
return players.size();
}
/**
* @return the info
*/
public WorldInfo getInfo() {
return info;
}
/**
* @return the session
*/
public IoSession getSession() {
return session;
}
/**
* @param session the session to set
*/
public void setSession(IoSession session) {
this.session = session;
}
/**
* Gets the players value.
* @return The players.
*/
public Map<String, PlayerSession> getPlayers() {
return players;
}
}

Some files were not shown because too many files have changed in this diff Show more