Salvation branch.

This commit is contained in:
MobiusDev
2018-09-11 21:41:16 +00:00
parent 29736fdc9f
commit efa20e9a2a
20734 changed files with 3917181 additions and 0 deletions

View File

@ -0,0 +1,50 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.concurrent;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.RejectedExecutionHandler;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.logging.Logger;
/**
* @author NB4L1
*/
public final class RejectedExecutionHandlerImpl implements RejectedExecutionHandler
{
private static final Logger LOGGER = Logger.getLogger(RejectedExecutionHandlerImpl.class.getName());
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
{
if (executor.isShutdown())
{
return;
}
LOGGER.warning(r + " from " + executor + " " + new RejectedExecutionException());
if (Thread.currentThread().getPriority() > Thread.NORM_PRIORITY)
{
new Thread(r).start();
}
else
{
r.run();
}
}
}

View File

@ -0,0 +1,50 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.concurrent;
import java.lang.Thread.UncaughtExceptionHandler;
/**
* @author UnAfraid
*/
public final class RunnableWrapper implements Runnable
{
private final Runnable _runnable;
public RunnableWrapper(Runnable runnable)
{
_runnable = runnable;
}
@Override
public void run()
{
try
{
_runnable.run();
}
catch (final Throwable e)
{
final Thread t = Thread.currentThread();
final UncaughtExceptionHandler h = t.getUncaughtExceptionHandler();
if (h != null)
{
h.uncaughtException(t, e);
}
}
}
}

View File

@ -0,0 +1,234 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.concurrent;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
import com.l2jmobius.Config;
/**
* This class handles thread pooling system. It relies on two ThreadPoolExecutor arrays, which poolers number is generated using config.
* <p>
* Those arrays hold following pools:
* </p>
* <ul>
* <li>Scheduled pool keeps a track about incoming, future events.</li>
* <li>Instant pool handles short-life events.</li>
* </ul>
*/
public final class ThreadPool
{
private static final Logger LOGGER = Logger.getLogger(ThreadPool.class.getName());
private static ScheduledThreadPoolExecutor[] SCHEDULED_POOLS;
private static ThreadPoolExecutor[] INSTANT_POOLS;
private static int THREAD_POOL_RANDOMIZER;
/**
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
*/
public static void init()
{
// Feed scheduled pool.
int scheduledPoolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
if (scheduledPoolCount == -1)
{
scheduledPoolCount = Runtime.getRuntime().availableProcessors();
}
SCHEDULED_POOLS = new ScheduledThreadPoolExecutor[scheduledPoolCount];
for (int i = 0; i < scheduledPoolCount; i++)
{
SCHEDULED_POOLS[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
}
// Feed instant pool.
int instantPoolCount = Config.INSTANT_THREAD_POOL_COUNT;
if (instantPoolCount == -1)
{
instantPoolCount = Runtime.getRuntime().availableProcessors();
}
INSTANT_POOLS = new ThreadPoolExecutor[instantPoolCount];
for (int i = 0; i < instantPoolCount; i++)
{
INSTANT_POOLS[i] = new ThreadPoolExecutor(Config.THREADS_PER_INSTANT_THREAD_POOL, Config.THREADS_PER_INSTANT_THREAD_POOL, 0, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(100000));
}
// Prestart core threads.
for (ScheduledThreadPoolExecutor threadPool : SCHEDULED_POOLS)
{
threadPool.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
threadPool.prestartAllCoreThreads();
}
for (ThreadPoolExecutor threadPool : INSTANT_POOLS)
{
threadPool.setRejectedExecutionHandler(new RejectedExecutionHandlerImpl());
threadPool.prestartAllCoreThreads();
}
// Launch purge task.
scheduleAtFixedRate(() ->
{
purge();
}, 600000, 600000);
LOGGER.info("ThreadPool: Initialized");
LOGGER.info("..." + scheduledPoolCount + " scheduled pool executors with " + (scheduledPoolCount * Config.THREADS_PER_SCHEDULED_THREAD_POOL) + " total threads.");
LOGGER.info("..." + instantPoolCount + " instant pool executors with " + (instantPoolCount * Config.THREADS_PER_INSTANT_THREAD_POOL) + " total threads.");
}
public static void purge()
{
for (ScheduledThreadPoolExecutor threadPool1 : SCHEDULED_POOLS)
{
threadPool1.purge();
}
for (ThreadPoolExecutor threadPool2 : INSTANT_POOLS)
{
threadPool2.purge();
}
}
/**
* Schedules a one-shot action that becomes enabled after a delay. The pool is chosen based on pools activity.
* @param r : the task to execute.
* @param delay : the time from now to delay execution.
* @return a ScheduledFuture representing pending completion of the task and whose get() method will return null upon completion.
*/
public static ScheduledFuture<?> schedule(Runnable r, long delay)
{
try
{
return getPool(SCHEDULED_POOLS).schedule(new RunnableWrapper(r), delay, TimeUnit.MILLISECONDS);
}
catch (Exception e)
{
return null;
}
}
/**
* Schedules a periodic action that becomes enabled after a delay. The pool is chosen based on pools activity.
* @param r : the task to execute.
* @param delay : the time from now to delay execution.
* @param period : the period between successive executions.
* @return a ScheduledFuture representing pending completion of the task and whose get() method will throw an exception upon cancellation.
*/
public static ScheduledFuture<?> scheduleAtFixedRate(Runnable r, long delay, long period)
{
try
{
return getPool(SCHEDULED_POOLS).scheduleAtFixedRate(new RunnableWrapper(r), delay, period, TimeUnit.MILLISECONDS);
}
catch (Exception e)
{
return null;
}
}
/**
* Executes the given task sometime in the future.
* @param r : the task to execute.
*/
public static void execute(Runnable r)
{
try
{
getPool(INSTANT_POOLS).execute(new RunnableWrapper(r));
}
catch (Exception e)
{
}
}
/**
* @param <T> : The pool type.
* @param threadPools : The pool array to check.
* @return the less fed pool.
*/
private static <T> T getPool(T[] threadPools)
{
return threadPools[THREAD_POOL_RANDOMIZER++ % threadPools.length];
}
public static String[] getStats()
{
final String[] stats = new String[(SCHEDULED_POOLS.length + INSTANT_POOLS.length) * 10];
int pos = 0;
for (int i = 0; i < SCHEDULED_POOLS.length; i++)
{
final ScheduledThreadPoolExecutor threadPool = SCHEDULED_POOLS[i];
stats[pos++] = "Scheduled pool #" + i + ":";
stats[pos++] = " |- ActiveCount: ...... " + threadPool.getActiveCount();
stats[pos++] = " |- CorePoolSize: ..... " + threadPool.getCorePoolSize();
stats[pos++] = " |- PoolSize: ......... " + threadPool.getPoolSize();
stats[pos++] = " |- LargestPoolSize: .. " + threadPool.getLargestPoolSize();
stats[pos++] = " |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize();
stats[pos++] = " |- CompletedTaskCount: " + threadPool.getCompletedTaskCount();
stats[pos++] = " |- QueuedTaskCount: .. " + threadPool.getQueue().size();
stats[pos++] = " |- TaskCount: ........ " + threadPool.getTaskCount();
stats[pos++] = " | -------";
}
for (int i = 0; i < INSTANT_POOLS.length; i++)
{
final ThreadPoolExecutor threadPool = INSTANT_POOLS[i];
stats[pos++] = "Instant pool #" + i + ":";
stats[pos++] = " |- ActiveCount: ...... " + threadPool.getActiveCount();
stats[pos++] = " |- CorePoolSize: ..... " + threadPool.getCorePoolSize();
stats[pos++] = " |- PoolSize: ......... " + threadPool.getPoolSize();
stats[pos++] = " |- LargestPoolSize: .. " + threadPool.getLargestPoolSize();
stats[pos++] = " |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize();
stats[pos++] = " |- CompletedTaskCount: " + threadPool.getCompletedTaskCount();
stats[pos++] = " |- QueuedTaskCount: .. " + threadPool.getQueue().size();
stats[pos++] = " |- TaskCount: ........ " + threadPool.getTaskCount();
stats[pos++] = " | -------";
}
return stats;
}
/**
* Shutdown thread pooling system correctly. Send different informations.
*/
public static void shutdown()
{
try
{
LOGGER.info("ThreadPool: Shutting down.");
for (ScheduledThreadPoolExecutor threadPool : SCHEDULED_POOLS)
{
threadPool.shutdownNow();
}
for (ThreadPoolExecutor threadPool : INSTANT_POOLS)
{
threadPool.shutdownNow();
}
}
catch (Throwable t)
{
t.printStackTrace();
}
}
}

View File

@ -0,0 +1,82 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.database;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import com.l2jmobius.Config;
import com.l2jmobius.Server;
/**
* @author Mobius
*/
public class DatabaseBackup
{
public static void performBackup()
{
// Delete old files.
if (Config.BACKUP_DAYS > 0)
{
final long cut = LocalDateTime.now().minusDays(Config.BACKUP_DAYS).toEpochSecond(ZoneOffset.UTC);
final Path path = Paths.get(Config.BACKUP_PATH);
try
{
Files.list(path).filter(n ->
{
try
{
return Files.getLastModifiedTime(n).to(TimeUnit.SECONDS) < cut;
}
catch (Exception ex)
{
return false;
}
}).forEach(n ->
{
try
{
Files.delete(n);
}
catch (Exception ex)
{
}
});
}
catch (Exception e)
{
}
}
// Dump to file.
final String mysqldumpPath = System.getProperty("os.name").toLowerCase().contains("win") ? Config.MYSQL_BIN_PATH : "";
try
{
final Process process = Runtime.getRuntime().exec(mysqldumpPath + "mysqldump -u " + Config.DATABASE_LOGIN + (Config.DATABASE_PASSWORD.trim().isEmpty() ? "" : " -p" + Config.DATABASE_PASSWORD) + " " + Config.DATABASE_URL.replace("jdbc:mariadb://", "").replaceAll(".*\\/|\\?.*", "") + " -r " + Config.BACKUP_PATH + (Server.serverMode == Server.MODE_GAMESERVER ? "game" : "login") + new SimpleDateFormat("_yyyy_MM_dd_HH_mm'.sql'").format(new Date()));
process.waitFor();
}
catch (Exception e)
{
}
}
}

View File

@ -0,0 +1,83 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.database;
import java.sql.Connection;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.zaxxer.hikari.HikariDataSource;
/**
* @author Mobius
*/
public class DatabaseFactory
{
private static final Logger LOGGER = Logger.getLogger(DatabaseFactory.class.getName());
private static final HikariDataSource _hds = new HikariDataSource();
public static void init()
{
_hds.setDriverClassName(Config.DATABASE_DRIVER);
_hds.setJdbcUrl(Config.DATABASE_URL);
_hds.setUsername(Config.DATABASE_LOGIN);
_hds.setPassword(Config.DATABASE_PASSWORD);
_hds.setMaximumPoolSize(Config.DATABASE_MAX_CONNECTIONS);
_hds.setIdleTimeout(Config.DATABASE_MAX_IDLE_TIME);
// Test if connection is valid.
try
{
_hds.getConnection().close();
LOGGER.info("Database: Initialized.");
}
catch (Exception e)
{
e.printStackTrace();
}
}
public static Connection getConnection()
{
Connection con = null;
while (con == null)
{
try
{
con = _hds.getConnection();
}
catch (Exception e)
{
LOGGER.severe("DatabaseFactory: Cound not get a connection. " + e);
}
}
return con;
}
public static void close()
{
try
{
_hds.close();
}
catch (Exception e)
{
LOGGER.severe("DatabaseFactory: There was a problem closing the data source. " + e);
}
}
}

View File

@ -0,0 +1,107 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import java.util.logging.Logger;
/**
* This class ...
* @version $Revision: 1.2.4.1 $ $Date: 2005/03/27 15:30:12 $
*/
public abstract class BaseRecievePacket
{
private static final Logger LOGGER = Logger.getLogger(BaseRecievePacket.class.getName());
private final byte[] _decrypt;
private int _off;
public BaseRecievePacket(byte[] decrypt)
{
_decrypt = decrypt;
_off = 1; // skip packet type id
}
public int readD()
{
int result = _decrypt[_off++] & 0xff;
result |= (_decrypt[_off++] << 8) & 0xff00;
result |= (_decrypt[_off++] << 0x10) & 0xff0000;
result |= (_decrypt[_off++] << 0x18) & 0xff000000;
return result;
}
public int readC()
{
return _decrypt[_off++] & 0xff;
}
public int readH()
{
return (_decrypt[_off++] & 0xff) | ((_decrypt[_off++] << 8) & 0xff00);
}
public double readF()
{
long result = _decrypt[_off++] & 0xff;
result |= (_decrypt[_off++] & 0xffL) << 8L;
result |= (_decrypt[_off++] & 0xffL) << 16L;
result |= (_decrypt[_off++] & 0xffL) << 24L;
result |= (_decrypt[_off++] & 0xffL) << 32L;
result |= (_decrypt[_off++] & 0xffL) << 40L;
result |= (_decrypt[_off++] & 0xffL) << 48L;
result |= (_decrypt[_off++] & 0xffL) << 56L;
return Double.longBitsToDouble(result);
}
public String readS()
{
String result = null;
try
{
result = new String(_decrypt, _off, _decrypt.length - _off, "UTF-16LE");
result = result.substring(0, result.indexOf(0x00));
_off += (result.length() * 2) + 2;
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": " + e.getMessage());
}
return result;
}
public final byte[] readB(int length)
{
final byte[] result = new byte[length];
System.arraycopy(_decrypt, _off, result, 0, length);
_off += length;
return result;
}
public long readQ()
{
long result = _decrypt[_off++] & 0xff;
result |= (_decrypt[_off++] & 0xffL) << 8L;
result |= (_decrypt[_off++] & 0xffL) << 16L;
result |= (_decrypt[_off++] & 0xffL) << 24L;
result |= (_decrypt[_off++] & 0xffL) << 32L;
result |= (_decrypt[_off++] & 0xffL) << 40L;
result |= (_decrypt[_off++] & 0xffL) << 48L;
result |= (_decrypt[_off++] & 0xffL) << 56L;
return result;
}
}

View File

@ -0,0 +1,137 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.logging.Logger;
/**
* This class ...
* @version $Revision: 1.2.4.1 $ $Date: 2005/03/27 15:30:11 $
*/
public abstract class BaseSendablePacket
{
private static final Logger LOGGER = Logger.getLogger(BaseSendablePacket.class.getName());
private final ByteArrayOutputStream _bao;
protected BaseSendablePacket()
{
_bao = new ByteArrayOutputStream();
}
protected void writeD(int value)
{
_bao.write(value & 0xff);
_bao.write((value >> 8) & 0xff);
_bao.write((value >> 16) & 0xff);
_bao.write((value >> 24) & 0xff);
}
protected void writeH(int value)
{
_bao.write(value & 0xff);
_bao.write((value >> 8) & 0xff);
}
protected void writeC(int value)
{
_bao.write(value & 0xff);
}
protected void writeF(double org)
{
final long value = Double.doubleToRawLongBits(org);
_bao.write((int) (value & 0xff));
_bao.write((int) ((value >> 8) & 0xff));
_bao.write((int) ((value >> 16) & 0xff));
_bao.write((int) ((value >> 24) & 0xff));
_bao.write((int) ((value >> 32) & 0xff));
_bao.write((int) ((value >> 40) & 0xff));
_bao.write((int) ((value >> 48) & 0xff));
_bao.write((int) ((value >> 56) & 0xff));
}
protected void writeS(String text)
{
try
{
if (text != null)
{
_bao.write(text.getBytes("UTF-16LE"));
}
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": " + e.getMessage());
}
_bao.write(0);
_bao.write(0);
}
protected void writeB(byte[] array)
{
try
{
_bao.write(array);
}
catch (IOException e)
{
LOGGER.warning(getClass().getSimpleName() + ": " + e.getMessage());
}
}
protected void writeQ(long value)
{
_bao.write((int) (value & 0xff));
_bao.write((int) ((value >> 8) & 0xff));
_bao.write((int) ((value >> 16) & 0xff));
_bao.write((int) ((value >> 24) & 0xff));
_bao.write((int) ((value >> 32) & 0xff));
_bao.write((int) ((value >> 40) & 0xff));
_bao.write((int) ((value >> 48) & 0xff));
_bao.write((int) ((value >> 56) & 0xff));
}
public int getLength()
{
return _bao.size() + 2;
}
public byte[] getBytes()
{
// if (this instanceof Init)
// writeD(0x00); // reserve for XOR initial key
writeD(0x00); // reserve for checksum
final int padding = _bao.size() % 8;
if (padding != 0)
{
for (int i = padding; i < 8; i++)
{
writeC(0x00);
}
}
return _bao.toByteArray();
}
public abstract byte[] getContent() throws IOException;
}

View File

@ -0,0 +1,46 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
/**
* @author Nos
* @param <T>
*/
public abstract class ChannelInboundHandler<T extends ChannelInboundHandler<?>>extends SimpleChannelInboundHandler<IIncomingPacket<T>>
{
private Channel _channel;
@Override
public void channelActive(ChannelHandlerContext ctx)
{
_channel = ctx.channel();
}
public void setConnectionState(IConnectionState connectionState)
{
_channel.attr(IConnectionState.ATTRIBUTE_KEY).set(connectionState);
}
public IConnectionState getConnectionState()
{
return _channel != null ? _channel.attr(IConnectionState.ATTRIBUTE_KEY).get() : null;
}
}

View File

@ -0,0 +1,27 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import io.netty.util.AttributeKey;
/**
* @author Nos
*/
public interface IConnectionState
{
AttributeKey<IConnectionState> ATTRIBUTE_KEY = AttributeKey.valueOf(IConnectionState.class, "");
}

View File

@ -0,0 +1,29 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import io.netty.buffer.ByteBuf;
/**
* @author Nos
*/
public interface ICrypt
{
void encrypt(ByteBuf buf);
void decrypt(ByteBuf buf);
}

View File

@ -0,0 +1,34 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
/**
* @author Nos
* @param <T>
*/
public interface IIncomingPacket<T>
{
/**
* Reads a packet.
* @param client the client
* @param packet the packet reader
* @return {@code true} if packet was read successfully, {@code false} otherwise.
*/
boolean read(T client, PacketReader packet);
void run(T client) throws Exception;
}

View File

@ -0,0 +1,32 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import java.util.Set;
/**
* @author Nos
* @param <T>
*/
public interface IIncomingPackets<T>extends IConnectionState
{
int getPacketId();
IIncomingPacket<T> newIncomingPacket();
Set<IConnectionState> getConnectionStates();
}

View File

@ -0,0 +1,29 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
/**
* @author Nos
*/
public interface IOutgoingPacket
{
/**
* @param packet the packet writer
* @return {@code true} if packet was writen successfully, {@code false} otherwise.
*/
boolean write(PacketWriter packet);
}

View File

@ -0,0 +1,76 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import java.util.logging.Logger;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
/**
* @author Nos
*/
public class NetworkManager
{
private final Logger LOGGER = Logger.getLogger(getClass().getName());
private final ServerBootstrap _serverBootstrap;
private final String _host;
private final int _port;
private ChannelFuture _channelFuture;
public NetworkManager(EventLoopGroup bossGroup, EventLoopGroup workerGroup, ChannelInitializer<SocketChannel> clientInitializer, String host, int port)
{
// @formatter:off
_serverBootstrap = new ServerBootstrap()
.group(bossGroup, workerGroup)
.channel(NioServerSocketChannel.class)
.childHandler(clientInitializer)
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT);
// @formatter:on
_host = host;
_port = port;
}
public ChannelFuture getChannelFuture()
{
return _channelFuture;
}
public void start() throws InterruptedException
{
if ((_channelFuture != null) && !_channelFuture.isDone())
{
return;
}
_channelFuture = _serverBootstrap.bind(_host, _port).sync();
LOGGER.info(getClass().getSimpleName() + ": Listening on " + _host + ":" + _port);
}
public void stop() throws InterruptedException
{
_channelFuture.channel().close().sync();
}
}

View File

@ -0,0 +1,163 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import io.netty.buffer.ByteBuf;
/**
* @author Nos
*/
public final class PacketReader
{
private final ByteBuf _buf;
public PacketReader(ByteBuf buf)
{
_buf = buf;
}
/**
* Gets the readable bytes.
* @return the readable bytes
*/
public int getReadableBytes()
{
return _buf.readableBytes();
}
/**
* Reads an unsigned byte.
* @return the unsigned byte
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code 1}
*/
public short readC()
{
return _buf.readUnsignedByte();
}
/**
* Reads an unsigned short.
* @return the unsigned short
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code 2}
*/
public int readH()
{
return _buf.readUnsignedShortLE();
}
/**
* Reads an integer.
* @return the integer
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code 4}
*/
public int readD()
{
return _buf.readIntLE();
}
/**
* Reads a long.
* @return the long
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code 8}
*/
public long readQ()
{
return _buf.readLongLE();
}
/**
* Reads a float.
* @return the float
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code 4}
*/
public float readE()
{
return Float.intBitsToFloat(_buf.readIntLE());
}
/**
* Reads a double.
* @return the double
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code 8}
*/
public double readF()
{
return Double.longBitsToDouble(_buf.readLongLE());
}
/**
* Reads a string.
* @return the string
* @throws IndexOutOfBoundsException if string {@code null} terminator is not found within {@code readableBytes}
*/
public String readS()
{
final StringBuilder sb = new StringBuilder();
char chr;
while ((chr = Character.reverseBytes(_buf.readChar())) != 0)
{
sb.append(chr);
}
return sb.toString();
}
/**
* Reads a fixed length string.
* @return the string
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code 2 + String.length * 2}
*/
public String readString()
{
final StringBuilder sb = new StringBuilder();
final int stringLength = _buf.readShortLE();
if ((stringLength * 2) > _buf.readableBytes())
{
throw new IndexOutOfBoundsException("readerIndex(" + _buf.readerIndex() + ") + length(" + (stringLength * 2) + ") exceeds writerIndex(" + _buf.writerIndex() + "): " + _buf);
}
for (int i = 0; i < stringLength; i++)
{
sb.append(Character.reverseBytes(_buf.readChar()));
}
return sb.toString();
}
/**
* Reads a byte array.
* @param length the length
* @return the byte array
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code length}
*/
public byte[] readB(int length)
{
final byte[] result = new byte[length];
_buf.readBytes(result);
return result;
}
/**
* Reads a byte array.
* @param dst the destination
* @param dstIndex the destination index to start writing the bytes from
* @param length the length
* @throws IndexOutOfBoundsException if {@code readableBytes} is less than {@code length}, if the specified dstIndex is less than 0 or if {@code dstIndex + length} is greater than {@code dst.length}
*/
public void readB(byte[] dst, int dstIndex, int length)
{
_buf.readBytes(dst, dstIndex, length);
}
}

View File

@ -0,0 +1,141 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network;
import io.netty.buffer.ByteBuf;
/**
* @author Nos
*/
public final class PacketWriter
{
private final ByteBuf _buf;
public PacketWriter(ByteBuf buf)
{
_buf = buf;
}
/**
* Gets the writable bytes.
* @return the writable bytes
*/
public int getWritableBytes()
{
return _buf.writableBytes();
}
/**
* Writes a byte.
* @param value the byte (The 24 high-order bits are ignored)
*/
public void writeC(int value)
{
_buf.writeByte(value);
}
/**
* Writes a short.
* @param value the short (The 16 high-order bits are ignored)
*/
public void writeH(int value)
{
_buf.writeShortLE(value);
}
/**
* Writes an integer.
* @param value the integer
*/
public void writeD(int value)
{
_buf.writeIntLE(value);
}
/**
* Writes a long.
* @param value the long
*/
public void writeQ(long value)
{
_buf.writeLongLE(value);
}
/**
* Writes a float.
* @param value the float
*/
public void writeE(float value)
{
_buf.writeIntLE(Float.floatToIntBits(value));
}
/**
* Writes a double.
* @param value the double
*/
public void writeF(double value)
{
_buf.writeLongLE(Double.doubleToLongBits(value));
}
/**
* Writes a string.
* @param value the string
*/
public void writeS(String value)
{
if (value != null)
{
for (int i = 0; i < value.length(); i++)
{
_buf.writeChar(Character.reverseBytes(value.charAt(i)));
}
}
_buf.writeChar(0);
}
/**
* Writes a string with fixed length specified as [short length, char[length] data].
* @param value the string
*/
public void writeString(String value)
{
if (value != null)
{
_buf.writeShortLE(value.length());
for (int i = 0; i < value.length(); i++)
{
_buf.writeChar(Character.reverseBytes(value.charAt(i)));
}
}
else
{
_buf.writeShort(0);
}
}
/**
* Writes a byte array.
* @param bytes the byte array
*/
public void writeB(byte[] bytes)
{
_buf.writeBytes(bytes);
}
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network.codecs;
import java.util.List;
import com.l2jmobius.commons.network.ICrypt;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageCodec;
/**
* @author Nos
*/
public class CryptCodec extends ByteToMessageCodec<ByteBuf>
{
private final ICrypt _crypt;
public CryptCodec(ICrypt crypt)
{
super();
_crypt = crypt;
}
/*
* (non-Javadoc)
* @see io.netty.handler.codec.ByteToMessageCodec#encode(io.netty.channel.ChannelHandlerContext, java.lang.Object, io.netty.buffer.ByteBuf)
*/
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out)
{
// Check if there are any data to encrypt.
if (!msg.isReadable())
{
return;
}
msg.resetReaderIndex();
_crypt.encrypt(msg);
msg.resetReaderIndex();
out.writeBytes(msg);
}
/*
* (non-Javadoc)
* @see io.netty.handler.codec.ByteToMessageCodec#decode(io.netty.channel.ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List)
*/
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
{
in.resetReaderIndex();
_crypt.decrypt(in);
in.readerIndex(in.writerIndex());
out.add(in.copy(0, in.writerIndex()));
}
}

View File

@ -0,0 +1,41 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network.codecs;
import java.util.List;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToMessageEncoder;
/**
* @author Nos
*/
@Sharable
public class LengthFieldBasedFrameEncoder extends MessageToMessageEncoder<ByteBuf>
{
@Override
protected void encode(ChannelHandlerContext ctx, ByteBuf msg, List<Object> out)
{
final ByteBuf buf = ctx.alloc().buffer(2);
final short length = (short) (msg.readableBytes() + 2);
buf.writeShortLE(length);
out.add(buf);
out.add(msg.retain());
}
}

View File

@ -0,0 +1,91 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network.codecs;
import java.util.List;
import java.util.logging.Logger;
import com.l2jmobius.commons.network.IConnectionState;
import com.l2jmobius.commons.network.IIncomingPacket;
import com.l2jmobius.commons.network.IIncomingPackets;
import com.l2jmobius.commons.network.PacketReader;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.ByteToMessageDecoder;
/**
* @author Nos
* @param <T>
*/
public class PacketDecoder<T>extends ByteToMessageDecoder
{
private static final Logger LOGGER = Logger.getLogger(PacketDecoder.class.getName());
private final IIncomingPackets<T>[] _incomingPackets;
private final T _client;
public PacketDecoder(IIncomingPackets<T>[] incomingPackets, T client)
{
_incomingPackets = incomingPackets;
_client = client;
}
@Override
protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out)
{
if ((in == null) || !in.isReadable())
{
return;
}
try
{
final short packetId = in.readUnsignedByte();
if (packetId >= _incomingPackets.length)
{
LOGGER.finer("Unknown packet: " + Integer.toHexString(packetId));
return;
}
final IIncomingPackets<T> incomingPacket = _incomingPackets[packetId];
if (incomingPacket == null)
{
LOGGER.finer("Unknown packet: " + Integer.toHexString(packetId));
return;
}
final IConnectionState connectionState = ctx.channel().attr(IConnectionState.ATTRIBUTE_KEY).get();
if ((connectionState == null) || !incomingPacket.getConnectionStates().contains(connectionState))
{
// LOGGER.warning(incomingPacket + ": Connection at invalid state: " + connectionState + " Required States: " + incomingPacket.getConnectionStates());
return;
}
final IIncomingPacket<T> packet = incomingPacket.newIncomingPacket();
if ((packet != null) && packet.read(_client, new PacketReader(in)))
{
out.add(packet);
}
}
finally
{
// We always consider that we read whole packet.
in.readerIndex(in.writerIndex());
}
}
}

View File

@ -0,0 +1,71 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.network.codecs;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.commons.network.IOutgoingPacket;
import com.l2jmobius.commons.network.PacketWriter;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.MessageToByteEncoder;
/**
* @author Nos
*/
@Sharable
public class PacketEncoder extends MessageToByteEncoder<IOutgoingPacket>
{
private static final Logger LOGGER = Logger.getLogger(PacketEncoder.class.getName());
private final int _maxPacketSize;
public PacketEncoder(int maxPacketSize)
{
super();
_maxPacketSize = maxPacketSize;
}
@Override
protected void encode(ChannelHandlerContext ctx, IOutgoingPacket packet, ByteBuf out)
{
try
{
if (packet.write(new PacketWriter(out)))
{
if (out.writerIndex() > _maxPacketSize)
{
throw new IllegalStateException("Packet (" + packet + ") size (" + out.writerIndex() + ") is bigger than the limit (" + _maxPacketSize + ")");
}
}
else
{
// Avoid sending the packet
out.clear();
}
}
catch (Throwable e)
{
LOGGER.log(Level.WARNING, "Failed sending Packet(" + packet + ")", e);
// Avoid sending the packet if some exception happened
out.clear();
}
}
}

View File

@ -0,0 +1,590 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.nio.ByteBuffer;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.time.DayOfWeek;
import java.time.LocalDateTime;
import java.time.temporal.TemporalAdjusters;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Locale;
import java.util.StringJoiner;
import java.util.StringTokenizer;
import com.l2jmobius.Config;
public final class CommonUtil
{
private static final char[] ILLEGAL_CHARACTERS =
{
'/',
'\n',
'\r',
'\t',
'\0',
'\f',
'`',
'?',
'*',
'\\',
'<',
'>',
'|',
'\"',
':'
};
/**
* Method to generate the hexadecimal representation of a byte array.<br>
* 16 bytes per row, while ascii chars or "." is shown at the end of the line.
* @param data the byte array to be represented in hexadecimal representation
* @param len the number of bytes to represent in hexadecimal representation
* @return byte array represented in hexadecimal format
*/
public static String printData(byte[] data, int len)
{
return new String(HexUtils.bArr2HexEdChars(data, len));
}
/**
* This call is equivalent to Util.printData(data, data.length)
* @see CommonUtil#printData(byte[],int)
* @param data data to represent in hexadecimal
* @return byte array represented in hexadecimal format
*/
public static String printData(byte[] data)
{
return printData(data, data.length);
}
/**
* Method to represent the remaining bytes of a ByteBuffer as hexadecimal
* @param buf ByteBuffer to represent the remaining bytes of as hexadecimal
* @return hexadecimal representation of remaining bytes of the ByteBuffer
*/
public static String printData(ByteBuffer buf)
{
final byte[] data = new byte[buf.remaining()];
buf.get(data);
final String hex = printData(data, data.length);
buf.position(buf.position() - data.length);
return hex;
}
/**
* Method to generate a random sequence of bytes returned as byte array
* @param size number of random bytes to generate
* @return byte array with sequence of random bytes
*/
public static byte[] generateHex(int size)
{
final byte[] array = new byte[size];
Rnd.nextBytes(array);
// Don't allow 0s inside the array!
for (int i = 0; i < array.length; i++)
{
while (array[i] == 0)
{
array[i] = (byte) Rnd.get(Byte.MAX_VALUE);
}
}
return array;
}
/**
* Replaces most invalid characters for the given string with an underscore.
* @param str the string that may contain invalid characters
* @return the string with invalid character replaced by underscores
*/
public static String replaceIllegalCharacters(String str)
{
String valid = str;
for (char c : ILLEGAL_CHARACTERS)
{
valid = valid.replace(c, '_');
}
return valid;
}
/**
* Verify if a file name is valid.
* @param name the name of the file
* @return {@code true} if the file name is valid, {@code false} otherwise
*/
public static boolean isValidFileName(String name)
{
final File f = new File(name);
try
{
f.getCanonicalPath();
return true;
}
catch (IOException e)
{
return false;
}
}
/**
* Split words with a space.
* @param input the string to split
* @return the split string
*/
public static String splitWords(String input)
{
return input.replaceAll("(\\p{Ll})(\\p{Lu})", "$1 $2");
}
/**
* Gets the next or same closest date from the specified days in {@code daysOfWeek Array} at specified {@code hour} and {@code min}.
* @param daysOfWeek the days of week
* @param hour the hour
* @param min the min
* @return the next or same date from the days of week at specified time
* @throws IllegalArgumentException if the {@code daysOfWeek Array} is empty.
*/
public static LocalDateTime getNextClosestDateTime(DayOfWeek[] daysOfWeek, int hour, int min) throws IllegalArgumentException
{
return getNextClosestDateTime(Arrays.asList(daysOfWeek), hour, min);
}
/**
* Gets the next or same closest date from the specified days in {@code daysOfWeek List} at specified {@code hour} and {@code min}.
* @param daysOfWeek the days of week
* @param hour the hour
* @param min the min
* @return the next or same date from the days of week at specified time
* @throws IllegalArgumentException if the {@code daysOfWeek List} is empty.
*/
public static LocalDateTime getNextClosestDateTime(List<DayOfWeek> daysOfWeek, int hour, int min) throws IllegalArgumentException
{
if (daysOfWeek.isEmpty())
{
throw new IllegalArgumentException("daysOfWeek should not be empty.");
}
final LocalDateTime dateNow = LocalDateTime.now();
final LocalDateTime dateNowWithDifferentTime = dateNow.withHour(hour).withMinute(min).withSecond(0);
// @formatter:off
return daysOfWeek.stream()
.map(d -> dateNowWithDifferentTime.with(TemporalAdjusters.nextOrSame(d)))
.filter(d -> d.isAfter(dateNow))
.min(Comparator.naturalOrder())
.orElse(dateNowWithDifferentTime.with(TemporalAdjusters.next(daysOfWeek.get(0))));
// @formatter:on
}
/**
* Method to get the stack trace of a Throwable into a String
* @param t Throwable to get the stacktrace from
* @return stack trace from Throwable as String
*/
public static String getStackTrace(Throwable t)
{
final StringWriter sw = new StringWriter();
t.printStackTrace(new PrintWriter(sw));
return sw.toString();
}
public static String getTraceString(StackTraceElement[] stackTraceElements)
{
final StringJoiner sj = new StringJoiner(Config.EOL);
for (StackTraceElement stackTraceElement : stackTraceElements)
{
sj.add(stackTraceElement.toString());
}
return sj.toString();
}
public static int min(int value1, int value2, int... values)
{
int min = Math.min(value1, value2);
for (int value : values)
{
if (min > value)
{
min = value;
}
}
return min;
}
public static int max(int value1, int value2, int... values)
{
int max = Math.max(value1, value2);
for (int value : values)
{
if (max < value)
{
max = value;
}
}
return max;
}
public static long min(long value1, long value2, long... values)
{
long min = Math.min(value1, value2);
for (long value : values)
{
if (min > value)
{
min = value;
}
}
return min;
}
public static long max(long value1, long value2, long... values)
{
long max = Math.max(value1, value2);
for (long value : values)
{
if (max < value)
{
max = value;
}
}
return max;
}
public static float min(float value1, float value2, float... values)
{
float min = Math.min(value1, value2);
for (float value : values)
{
if (min > value)
{
min = value;
}
}
return min;
}
public static float max(float value1, float value2, float... values)
{
float max = Math.max(value1, value2);
for (float value : values)
{
if (max < value)
{
max = value;
}
}
return max;
}
public static double min(double value1, double value2, double... values)
{
double min = Math.min(value1, value2);
for (double value : values)
{
if (min > value)
{
min = value;
}
}
return min;
}
public static double max(double value1, double value2, double... values)
{
double max = Math.max(value1, value2);
for (double value : values)
{
if (max < value)
{
max = value;
}
}
return max;
}
public static int getIndexOfMaxValue(int... array)
{
int index = 0;
for (int i = 1; i < array.length; i++)
{
if (array[i] > array[index])
{
index = i;
}
}
return index;
}
public static int getIndexOfMinValue(int... array)
{
int index = 0;
for (int i = 1; i < array.length; i++)
{
if (array[i] < array[index])
{
index = i;
}
}
return index;
}
/**
* Re-Maps a value from one range to another.
* @param input
* @param inputMin
* @param inputMax
* @param outputMin
* @param outputMax
* @return The mapped value
*/
public static int map(int input, int inputMin, int inputMax, int outputMin, int outputMax)
{
input = constrain(input, inputMin, inputMax);
return (((input - inputMin) * (outputMax - outputMin)) / (inputMax - inputMin)) + outputMin;
}
/**
* Re-Maps a value from one range to another.
* @param input
* @param inputMin
* @param inputMax
* @param outputMin
* @param outputMax
* @return The mapped value
*/
public static long map(long input, long inputMin, long inputMax, long outputMin, long outputMax)
{
input = constrain(input, inputMin, inputMax);
return (((input - inputMin) * (outputMax - outputMin)) / (inputMax - inputMin)) + outputMin;
}
/**
* Re-Maps a value from one range to another.
* @param input
* @param inputMin
* @param inputMax
* @param outputMin
* @param outputMax
* @return The mapped value
*/
public static double map(double input, double inputMin, double inputMax, double outputMin, double outputMax)
{
input = constrain(input, inputMin, inputMax);
return (((input - inputMin) * (outputMax - outputMin)) / (inputMax - inputMin)) + outputMin;
}
/**
* Constrains a number to be within a range.
* @param input the number to constrain, all data types
* @param min the lower end of the range, all data types
* @param max the upper end of the range, all data types
* @return input: if input is between min and max, min: if input is less than min, max: if input is greater than max
*/
public static int constrain(int input, int min, int max)
{
return (input < min) ? min : (input > max) ? max : input;
}
/**
* Constrains a number to be within a range.
* @param input the number to constrain, all data types
* @param min the lower end of the range, all data types
* @param max the upper end of the range, all data types
* @return input: if input is between min and max, min: if input is less than min, max: if input is greater than max
*/
public static long constrain(long input, long min, long max)
{
return (input < min) ? min : (input > max) ? max : input;
}
/**
* Constrains a number to be within a range.
* @param input the number to constrain, all data types
* @param min the lower end of the range, all data types
* @param max the upper end of the range, all data types
* @return input: if input is between min and max, min: if input is less than min, max: if input is greater than max
*/
public static double constrain(double input, double min, double max)
{
return (input < min) ? min : (input > max) ? max : input;
}
/**
* @param array - the array to look into
* @param obj - the object to search for
* @return {@code true} if the {@code array} contains the {@code obj}, {@code false} otherwise.
*/
public static boolean startsWith(String[] array, String obj)
{
for (String element : array)
{
if (element.startsWith(obj))
{
return true;
}
}
return false;
}
/**
* @param <T>
* @param array - the array to look into
* @param obj - the object to search for
* @return {@code true} if the {@code array} contains the {@code obj}, {@code false} otherwise.
*/
public static <T> boolean contains(T[] array, T obj)
{
for (T element : array)
{
if (element.equals(obj))
{
return true;
}
}
return false;
}
/**
* @param array - the array to look into
* @param obj - the integer to search for
* @return {@code true} if the {@code array} contains the {@code obj}, {@code false} otherwise
*/
public static boolean contains(int[] array, int obj)
{
for (int element : array)
{
if (element == obj)
{
return true;
}
}
return false;
}
/**
* @param array - the array to look into
* @param obj - the object to search for
* @param ignoreCase
* @return {@code true} if the {@code array} contains the {@code obj}, {@code false} otherwise.
*/
public static boolean contains(String[] array, String obj, boolean ignoreCase)
{
for (String element : array)
{
if (element.equals(obj) || (ignoreCase && element.equalsIgnoreCase(obj)))
{
return true;
}
}
return false;
}
public static int parseNextInt(StringTokenizer st, int defaultVal)
{
try
{
final String value = st.nextToken().trim();
return Integer.parseInt(value);
}
catch (Exception e)
{
return defaultVal;
}
}
public static int parseInt(String value, int defaultValue)
{
try
{
return Integer.parseInt(value);
}
catch (Exception e)
{
return defaultValue;
}
}
/**
* @param str - the string whose first letter to capitalize
* @return a string with the first letter of the {@code str} capitalized
*/
public static String capitalizeFirst(String str)
{
if ((str == null) || str.isEmpty())
{
return str;
}
final char[] arr = str.toCharArray();
final char c = arr[0];
if (Character.isLetter(c))
{
arr[0] = Character.toUpperCase(c);
}
return new String(arr);
}
/**
* Based on implode() in PHP
* @param <T>
* @param iteratable
* @param delim
* @return a delimited string for a given array of string elements.
*/
public static <T> String implode(Iterable<T> iteratable, String delim)
{
final StringJoiner sj = new StringJoiner(delim);
iteratable.forEach(o -> sj.add(o.toString()));
return sj.toString();
}
/**
* Based on implode() in PHP
* @param <T>
* @param array
* @param delim
* @return a delimited string for a given array of string elements.
*/
public static <T> String implode(T[] array, String delim)
{
final StringJoiner sj = new StringJoiner(delim);
for (T o : array)
{
sj.add(o.toString());
}
return sj.toString();
}
/**
* @param val
* @param format
* @return
*/
public static String formatDouble(double val, String format)
{
final DecimalFormat formatter = new DecimalFormat(format, new DecimalFormatSymbols(Locale.ENGLISH));
return formatter.format(val);
}
}

View File

@ -0,0 +1,121 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.lang.management.LockInfo;
import java.lang.management.ManagementFactory;
import java.lang.management.MonitorInfo;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.time.Duration;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
/**
* Thread to check for deadlocked threads.
* @author -Nemesiss- L2M
*/
public class DeadLockDetector extends Thread
{
private static Logger LOGGER = Logger.getLogger(DeadLockDetector.class.getName());
private final Duration _checkInterval;
private final Runnable _deadLockCallback;
private final ThreadMXBean tmx;
public DeadLockDetector(Duration checkInterval, Runnable deadLockCallback)
{
super("DeadLockDetector");
_checkInterval = checkInterval;
_deadLockCallback = deadLockCallback;
tmx = ManagementFactory.getThreadMXBean();
}
@Override
public final void run()
{
boolean deadlock = false;
while (!deadlock)
{
try
{
final long[] ids = tmx.findDeadlockedThreads();
if (ids != null)
{
deadlock = true;
final ThreadInfo[] tis = tmx.getThreadInfo(ids, true, true);
final StringBuilder info = new StringBuilder();
info.append("DeadLock Found!");
info.append(Config.EOL);
for (ThreadInfo ti : tis)
{
info.append(ti.toString());
}
for (ThreadInfo ti : tis)
{
final LockInfo[] locks = ti.getLockedSynchronizers();
final MonitorInfo[] monitors = ti.getLockedMonitors();
if ((locks.length == 0) && (monitors.length == 0))
{
continue;
}
ThreadInfo dl = ti;
info.append("Java-level deadlock:");
info.append(Config.EOL);
info.append('\t');
info.append(dl.getThreadName());
info.append(" is waiting to lock ");
info.append(dl.getLockInfo().toString());
info.append(" which is held by ");
info.append(dl.getLockOwnerName());
info.append(Config.EOL);
while ((dl = tmx.getThreadInfo(new long[]
{
dl.getLockOwnerId()
}, true, true)[0]).getThreadId() != ti.getThreadId())
{
info.append('\t');
info.append(dl.getThreadName());
info.append(" is waiting to lock ");
info.append(dl.getLockInfo().toString());
info.append(" which is held by ");
info.append(dl.getLockOwnerName());
info.append(Config.EOL);
}
}
LOGGER.warning(info.toString());
if (_deadLockCallback != null)
{
_deadLockCallback.run();
}
}
Thread.sleep(_checkInterval.toMillis());
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "DeadLockDetector: ", e);
}
}
}
}

View File

@ -0,0 +1,145 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
import java.util.Queue;
/**
* @author UnAfraid
* @param <E>
*/
public final class EmptyQueue<E> implements Queue<E>
{
private static final Queue<Object> EMPTY_QUEUE = new EmptyQueue<>();
@SuppressWarnings("unchecked")
public static <E> Queue<E> emptyQueue()
{
return (Queue<E>) EMPTY_QUEUE;
}
@Override
public int size()
{
return 0;
}
@Override
public boolean isEmpty()
{
return true;
}
@Override
public boolean contains(Object o)
{
return false;
}
@Override
public Iterator<E> iterator()
{
return Collections.<E> emptyIterator();
}
@Override
public Object[] toArray()
{
return new Object[0];
}
@Override
public <T> T[] toArray(T[] a)
{
throw new UnsupportedOperationException();
}
@Override
public boolean remove(Object o)
{
throw new UnsupportedOperationException();
}
@Override
public boolean containsAll(Collection<?> c)
{
return false;
}
@Override
public boolean addAll(Collection<? extends E> c)
{
throw new UnsupportedOperationException();
}
@Override
public boolean removeAll(Collection<?> c)
{
throw new UnsupportedOperationException();
}
@Override
public boolean retainAll(Collection<?> c)
{
throw new UnsupportedOperationException();
}
@Override
public void clear()
{
throw new UnsupportedOperationException();
}
@Override
public boolean add(E e)
{
throw new UnsupportedOperationException();
}
@Override
public boolean offer(E e)
{
throw new UnsupportedOperationException();
}
@Override
public E remove()
{
throw new UnsupportedOperationException();
}
@Override
public E poll()
{
throw new UnsupportedOperationException();
}
@Override
public E element()
{
throw new UnsupportedOperationException();
}
@Override
public E peek()
{
throw new UnsupportedOperationException();
}
}

View File

@ -0,0 +1,260 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.util.Arrays;
/**
* @author HorridoJoho
*/
public class HexUtils
{
// lookup table for hex characters
private static final char[] _NIBBLE_CHAR_LOOKUP =
{
'0',
'1',
'2',
'3',
'4',
'5',
'6',
'7',
'8',
'9',
'A',
'B',
'C',
'D',
'E',
'F'
};
private static final char[] _NEW_LINE_CHARS = System.getProperty("line.separator").toCharArray();
/**
* Method to generate the hexadecimal character presentation of a byte<br>
* This call is equivalent to {@link HexUtils#b2HexChars(byte, char[], int)} with parameters (data, null, 0)
* @param data byte to generate the hexadecimal character presentation from
* @return a new char array with exactly 2 elements
*/
public static char[] b2HexChars(byte data)
{
return b2HexChars(data, null, 0);
}
/**
* Method to generate the hexadecimal character presentation of a byte
* @param data byte to generate the hexadecimal character presentation from
* @param dstHexChars the char array the hexadecimal character presentation should be copied to, if this is null, dstOffset is ignored and a new char array with 2 elements is created
* @param dstOffset offset at which the hexadecimal character presentation is copied to dstHexChars
* @return the char array the hexadecimal character presentation was copied to
*/
public static char[] b2HexChars(byte data, char[] dstHexChars, int dstOffset)
{
if (dstHexChars == null)
{
dstHexChars = new char[2];
dstOffset = 0;
}
// /////////////////////////////
// NIBBLE LOOKUP
dstHexChars[dstOffset] = _NIBBLE_CHAR_LOOKUP[(data & 0xF0) >> 4];
dstHexChars[dstOffset + 1] = _NIBBLE_CHAR_LOOKUP[data & 0x0F];
return dstHexChars;
}
/**
* Method to generate the hexadecimal character presentation of an integer This call is equivalent to {@link HexUtils#int2HexChars(int, char[], int)} with parameters (data, null, 0)
* @param data integer to generate the hexadecimal character presentation from
* @return new char array with 8 elements
*/
public static char[] int2HexChars(int data)
{
return int2HexChars(data, new char[8], 0);
}
/**
* Method to generate the hexadecimal character presentation of an integer
* @param data integer to generate the hexadecimal character presentation from
* @param dstHexChars the char array the hexadecimal character presentation should be copied to, if this is null, dstOffset is ignored and a new char array with 8 elements is created
* @param dstOffset offset at which the hexadecimal character presentation is copied to dstHexChars
* @return the char array the hexadecimal character presentation was copied to
*/
public static char[] int2HexChars(int data, char[] dstHexChars, int dstOffset)
{
if (dstHexChars == null)
{
dstHexChars = new char[8];
dstOffset = 0;
}
b2HexChars((byte) ((data & 0xFF000000) >> 24), dstHexChars, dstOffset);
b2HexChars((byte) ((data & 0x00FF0000) >> 16), dstHexChars, dstOffset + 2);
b2HexChars((byte) ((data & 0x0000FF00) >> 8), dstHexChars, dstOffset + 4);
b2HexChars((byte) (data & 0x000000FF), dstHexChars, dstOffset + 6);
return dstHexChars;
}
/**
* Method to generate the hexadecimal character presentation of a byte array<br>
* This call is equivalent to {@link HexUtils#bArr2HexChars(byte[], int, int, char[], int)} with parameters (data, offset, len, null, 0)
* @param data byte array to generate the hexadecimal character presentation from
* @param offset offset where to start in data array
* @param len number of bytes to generate the hexadecimal character presentation from
* @return a new char array with len*2 elements
*/
public static char[] bArr2HexChars(byte[] data, int offset, int len)
{
return bArr2HexChars(data, offset, len, null, 0);
}
/**
* Method to generate the hexadecimal character presentation of a byte array
* @param data byte array to generate the hexadecimal character presentation from
* @param offset offset where to start in data array
* @param len number of bytes to generate the hexadecimal character presentation from
* @param dstHexChars the char array the hexadecimal character presentation should be copied to, if this is null, dstOffset is ignored and a new char array with len*2 elements is created
* @param dstOffset offset at which the hexadecimal character presentation is copied to dstHexChars
* @return the char array the hexadecimal character presentation was copied to
*/
public static char[] bArr2HexChars(byte[] data, int offset, int len, char[] dstHexChars, int dstOffset)
{
if (dstHexChars == null)
{
dstHexChars = new char[len * 2];
dstOffset = 0;
}
for (int dataIdx = offset, charsIdx = dstOffset; dataIdx < (len + offset); ++dataIdx, ++charsIdx)
{
// /////////////////////////////
// NIBBLE LOOKUP, we duplicate the code from b2HexChars here, we want to save a few cycles(for charsIdx increment)
dstHexChars[charsIdx] = _NIBBLE_CHAR_LOOKUP[(data[dataIdx] & 0xF0) >> 4];
dstHexChars[++charsIdx] = _NIBBLE_CHAR_LOOKUP[data[dataIdx] & 0x0F];
}
return dstHexChars;
}
public static char[] bArr2AsciiChars(byte[] data, int offset, int len)
{
return bArr2AsciiChars(data, offset, len, new char[len], 0);
}
public static char[] bArr2AsciiChars(byte[] data, int offset, int len, char[] dstAsciiChars, int dstOffset)
{
if (dstAsciiChars == null)
{
dstAsciiChars = new char[len];
dstOffset = 0;
}
for (int dataIdx = offset, charsIdx = dstOffset; dataIdx < (len + offset); ++dataIdx, ++charsIdx)
{
if ((data[dataIdx] > 0x1f) && (data[dataIdx] < 0x80))
{
dstAsciiChars[charsIdx] = (char) data[dataIdx];
}
else
{
dstAsciiChars[charsIdx] = '.';
}
}
return dstAsciiChars;
}
private static final int _HEX_ED_BPL = 16;
private static final int _HEX_ED_CPB = 2;
/**
* Method to generate the hexadecimal character representation of a byte array like in a hex editor<br>
* Line Format: {OFFSET} {HEXADECIMAL} {ASCII}({NEWLINE})<br>
* {OFFSET} = offset of the first byte in line(8 chars)<br>
* {HEXADECIMAL} = hexadecimal character representation({@link #_HEX_ED_BPL}*2 chars)<br>
* {ASCII} = ascii character presentation({@link #_HEX_ED_BPL} chars)
* @param data byte array to generate the hexadecimal character representation
* @param len the number of bytes to generate the hexadecimal character representation from
* @return byte array which contains the hexadecimal character representation of the given byte array
*/
public static char[] bArr2HexEdChars(byte[] data, int len)
{
// {OFFSET} {HEXADECIMAL} {ASCII}{NEWLINE}
final int lineLength = 9 + (_HEX_ED_BPL * _HEX_ED_CPB) + 1 + _HEX_ED_BPL + _NEW_LINE_CHARS.length;
final int lenBplMod = len % _HEX_ED_BPL;
// create text buffer
// 1. don't allocate a full last line if not _HEX_ED_BPL bytes are shown in last line
// 2. no new line at end of buffer
// BUG: when the length is multiple of _HEX_ED_BPL we erase the whole ascii space with this
// char[] textData = new char[lineLength * numLines - (_HEX_ED_BPL - (len % _HEX_ED_BPL)) - _NEW_LINE_CHARS.length];
// FIXED HERE
int numLines;
char[] textData;
if (lenBplMod == 0)
{
numLines = len / _HEX_ED_BPL;
textData = new char[(lineLength * numLines) - _NEW_LINE_CHARS.length];
}
else
{
numLines = (len / _HEX_ED_BPL) + 1;
textData = new char[(lineLength * numLines) - (_HEX_ED_BPL - (lenBplMod)) - _NEW_LINE_CHARS.length];
}
// performance penalty, only doing space filling in the loop is faster
// Arrays.fill(textData, ' ');
int dataOffset;
int dataLen;
int lineStart;
int lineHexDataStart;
int lineAsciiDataStart;
for (int i = 0; i < numLines; ++i)
{
dataOffset = i * _HEX_ED_BPL;
dataLen = Math.min(len - dataOffset, _HEX_ED_BPL);
lineStart = i * lineLength;
lineHexDataStart = lineStart + 9;
lineAsciiDataStart = lineHexDataStart + (_HEX_ED_BPL * _HEX_ED_CPB) + 1;
int2HexChars(dataOffset, textData, lineStart); // the offset of this line
textData[lineHexDataStart - 1] = ' '; // separate
bArr2HexChars(data, dataOffset, dataLen, textData, lineHexDataStart); // the data in hex
bArr2AsciiChars(data, dataOffset, dataLen, textData, lineAsciiDataStart); // the data in ascii
if (i < (numLines - 1))
{
textData[lineAsciiDataStart - 1] = ' '; // separate
System.arraycopy(_NEW_LINE_CHARS, 0, textData, lineAsciiDataStart + _HEX_ED_BPL, _NEW_LINE_CHARS.length); // the new line
}
else if (dataLen < _HEX_ED_BPL)
{
// last line which shows less than _HEX_ED_BPL bytes
final int lineHexDataEnd = lineHexDataStart + (dataLen * _HEX_ED_CPB);
Arrays.fill(textData, lineHexDataEnd, lineHexDataEnd + ((_HEX_ED_BPL - dataLen) * _HEX_ED_CPB) + 1, ' '); // spaces, for the last line if there are not _HEX_ED_BPL bytes
}
else
{
// last line which shows _HEX_ED_BPL bytes
textData[lineAsciiDataStart - 1] = ' '; // separate
}
}
return textData;
}
}

View File

@ -0,0 +1,118 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.model.Location;
import com.l2jmobius.gameserver.model.holders.MinionHolder;
import com.l2jmobius.gameserver.model.holders.SkillHolder;
/**
* Interface for XML parsers.
* @author Zoey76
*/
public interface IGameXmlReader extends IXmlReader
{
/**
* Wrapper for {@link #parseFile(File)} method.
* @param path the relative path to the datapack root of the XML file to parse.
*/
default void parseDatapackFile(String path)
{
parseFile(new File(Config.DATAPACK_ROOT, path));
}
/**
* Wrapper for {@link #parseDirectory(File, boolean)}.
* @param path the path to the directory where the XML files are
* @param recursive parses all sub folders if there is
* @return {@code false} if it fails to find the directory, {@code true} otherwise
*/
default boolean parseDatapackDirectory(String path, boolean recursive)
{
return parseDirectory(new File(Config.DATAPACK_ROOT, path), recursive);
}
/**
* @param n
* @return a map of parameters
*/
default Map<String, Object> parseParameters(Node n)
{
final Map<String, Object> parameters = new HashMap<>();
for (Node parameters_node = n.getFirstChild(); parameters_node != null; parameters_node = parameters_node.getNextSibling())
{
NamedNodeMap attrs = parameters_node.getAttributes();
switch (parameters_node.getNodeName().toLowerCase())
{
case "param":
{
parameters.put(parseString(attrs, "name"), parseString(attrs, "value"));
break;
}
case "skill":
{
parameters.put(parseString(attrs, "name"), new SkillHolder(parseInteger(attrs, "id"), parseInteger(attrs, "level")));
break;
}
case "location":
{
parameters.put(parseString(attrs, "name"), new Location(parseInteger(attrs, "x"), parseInteger(attrs, "y"), parseInteger(attrs, "z"), parseInteger(attrs, "heading", 0)));
break;
}
case "minions":
{
final List<MinionHolder> minions = new ArrayList<>(1);
for (Node minions_node = parameters_node.getFirstChild(); minions_node != null; minions_node = minions_node.getNextSibling())
{
if (minions_node.getNodeName().equalsIgnoreCase("npc"))
{
attrs = minions_node.getAttributes();
minions.add(new MinionHolder(parseInteger(attrs, "id"), parseInteger(attrs, "count"), parseInteger(attrs, "respawnTime"), parseInteger(attrs, "weightPoint")));
}
}
if (!minions.isEmpty())
{
parameters.put(parseString(parameters_node.getAttributes(), "name"), minions);
}
break;
}
}
}
return parameters;
}
default Location parseLocation(Node n)
{
final NamedNodeMap attrs = n.getAttributes();
final int x = parseInteger(attrs, "x");
final int y = parseInteger(attrs, "y");
final int z = parseInteger(attrs, "z");
final int heading = parseInteger(attrs, "heading", 0);
return new Location(x, y, z, heading);
}
}

View File

@ -0,0 +1,156 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.net.InetAddress;
import java.net.UnknownHostException;
public class IPSubnet
{
private final byte[] _addr;
private final byte[] _mask;
private final boolean _isIPv4;
public IPSubnet(String input) throws UnknownHostException, NumberFormatException, ArrayIndexOutOfBoundsException
{
final int idx = input.indexOf("/");
if (idx > 0)
{
_addr = InetAddress.getByName(input.substring(0, idx)).getAddress();
_mask = getMask(Integer.parseInt(input.substring(idx + 1)), _addr.length);
_isIPv4 = _addr.length == 4;
if (!applyMask(_addr))
{
throw new UnknownHostException(input);
}
}
else
{
_addr = InetAddress.getByName(input).getAddress();
_mask = getMask(_addr.length * 8, _addr.length); // host, no need to check mask
_isIPv4 = _addr.length == 4;
}
}
public byte[] getAddress()
{
return _addr;
}
private boolean applyMask(byte[] addr)
{
// V4 vs V4 or V6 vs V6 checks
if (_isIPv4 == (addr.length == 4))
{
for (int i = 0; i < _addr.length; i++)
{
if ((addr[i] & _mask[i]) != _addr[i])
{
return false;
}
}
}
else
{
// check for embedded v4 in v6 addr (not done !)
if (_isIPv4)
{
// my V4 vs V6
for (int i = 0; i < _addr.length; i++)
{
if ((addr[i + 12] & _mask[i]) != _addr[i])
{
return false;
}
}
}
else
{
// my V6 vs V4
for (int i = 0; i < _addr.length; i++)
{
if ((addr[i] & _mask[i + 12]) != _addr[i + 12])
{
return false;
}
}
}
}
return true;
}
@Override
public String toString()
{
int size = 0;
for (byte element : _mask)
{
size += Integer.bitCount((element & 0xFF));
}
try
{
return InetAddress.getByAddress(_addr) + "/" + size;
}
catch (UnknownHostException e)
{
return "Invalid";
}
}
@Override
public boolean equals(Object o)
{
if (this == o)
{
return true;
}
if (o instanceof IPSubnet)
{
return applyMask(((IPSubnet) o).getAddress());
}
else if (o instanceof InetAddress)
{
return applyMask(((InetAddress) o).getAddress());
}
return false;
}
private static byte[] getMask(int n, int maxLength) throws UnknownHostException
{
if ((n > (maxLength << 3)) || (n < 0))
{
throw new UnknownHostException("Invalid netmask: " + n);
}
final byte[] result = new byte[maxLength];
for (int i = 0; i < maxLength; i++)
{
result[i] = (byte) 0xFF;
}
for (int i = (maxLength << 3) - 1; i >= n; i--)
{
result[i >> 3] = (byte) (result[i >> 3] << 1);
}
return result;
}
}

View File

@ -0,0 +1,706 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.io.File;
import java.io.FileFilter;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXParseException;
import com.l2jmobius.commons.util.file.filter.XMLFilter;
/**
* Interface for XML parsers.
* @author Zoey76
*/
public interface IXmlReader
{
Logger LOGGER = Logger.getLogger(IXmlReader.class.getName());
String JAXP_SCHEMA_LANGUAGE = "http://java.sun.com/xml/jaxp/properties/schemaLanguage";
String W3C_XML_SCHEMA = "http://www.w3.org/2001/XMLSchema";
/** The default file filter, ".xml" files only. */
XMLFilter XML_FILTER = new XMLFilter();
/**
* This method can be used to load/reload the data.<br>
* It's highly recommended to clear the data storage, either the list or map.
*/
void load();
/**
* Parses a single XML file.<br>
* If the file was successfully parsed, call {@link #parseDocument(Document, File)} for the parsed document.<br>
* <b>Validation is enforced.</b>
* @param f the XML file to parse.
*/
default void parseFile(File f)
{
if (!getCurrentFileFilter().accept(f))
{
LOGGER.warning("Could not parse " + f.getName() + " is not a file or it doesn't exist!");
return;
}
final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
dbf.setNamespaceAware(true);
dbf.setValidating(isValidating());
dbf.setIgnoringComments(isIgnoringComments());
try
{
dbf.setAttribute(JAXP_SCHEMA_LANGUAGE, W3C_XML_SCHEMA);
final DocumentBuilder db = dbf.newDocumentBuilder();
db.setErrorHandler(new XMLErrorHandler());
parseDocument(db.parse(f), f);
}
catch (SAXParseException e)
{
LOGGER.log(Level.WARNING, "Could not parse file: " + f.getName() + " at line: " + e.getLineNumber() + ", column: " + e.getColumnNumber() + " :", e);
return;
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Could not parse file: " + f.getName(), e);
return;
}
}
/**
* Checks if XML validation is enabled.
* @return {@code true} if its enabled, {@code false} otherwise
*/
default boolean isValidating()
{
return true;
}
/**
* Checks if XML comments are ignored.
* @return {@code true} if its comments are ignored, {@code false} otherwise
*/
default boolean isIgnoringComments()
{
return true;
}
/**
* Wrapper for {@link #parseDirectory(File, boolean)}.
* @param file the path to the directory where the XML files are.
* @return {@code false} if it fails to find the directory, {@code true} otherwise.
*/
default boolean parseDirectory(File file)
{
return parseDirectory(file, false);
}
/**
* Loads all XML files from {@code path} and calls {@link #parseFile(File)} for each one of them.
* @param dir the directory object to scan.
* @param recursive parses all sub folders if there is.
* @return {@code false} if it fails to find the directory, {@code true} otherwise.
*/
default boolean parseDirectory(File dir, boolean recursive)
{
if (!dir.exists())
{
LOGGER.warning("Folder " + dir.getAbsolutePath() + " doesn't exist!");
return false;
}
final File[] listOfFiles = dir.listFiles();
for (File f : listOfFiles)
{
if (recursive && f.isDirectory())
{
parseDirectory(f, recursive);
}
else if (getCurrentFileFilter().accept(f))
{
parseFile(f);
}
}
return true;
}
/**
* Abstract method that when implemented will parse the current document.<br>
* Is expected to be call from {@link #parseFile(File)}.
* @param doc the current document to parse
* @param f the current file
*/
void parseDocument(Document doc, File f);
/**
* Parses a boolean value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Boolean parseBoolean(Node node, Boolean defaultValue)
{
return node != null ? Boolean.valueOf(node.getNodeValue()) : defaultValue;
}
/**
* Parses a boolean value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Boolean parseBoolean(Node node)
{
return parseBoolean(node, null);
}
/**
* Parses a boolean value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Boolean parseBoolean(NamedNodeMap attrs, String name)
{
return parseBoolean(attrs.getNamedItem(name));
}
/**
* Parses a boolean value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Boolean parseBoolean(NamedNodeMap attrs, String name, Boolean defaultValue)
{
return parseBoolean(attrs.getNamedItem(name), defaultValue);
}
/**
* Parses a byte value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Byte parseByte(Node node, Byte defaultValue)
{
return node != null ? Byte.decode(node.getNodeValue()) : defaultValue;
}
/**
* Parses a byte value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Byte parseByte(Node node)
{
return parseByte(node, null);
}
/**
* Parses a byte value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Byte parseByte(NamedNodeMap attrs, String name)
{
return parseByte(attrs.getNamedItem(name));
}
/**
* Parses a byte value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Byte parseByte(NamedNodeMap attrs, String name, Byte defaultValue)
{
return parseByte(attrs.getNamedItem(name), defaultValue);
}
/**
* Parses a short value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Short parseShort(Node node, Short defaultValue)
{
return node != null ? Short.decode(node.getNodeValue()) : defaultValue;
}
/**
* Parses a short value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Short parseShort(Node node)
{
return parseShort(node, null);
}
/**
* Parses a short value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Short parseShort(NamedNodeMap attrs, String name)
{
return parseShort(attrs.getNamedItem(name));
}
/**
* Parses a short value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Short parseShort(NamedNodeMap attrs, String name, Short defaultValue)
{
return parseShort(attrs.getNamedItem(name), defaultValue);
}
/**
* Parses an int value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default int parseInt(Node node, Integer defaultValue)
{
return node != null ? Integer.decode(node.getNodeValue()) : defaultValue;
}
/**
* Parses an int value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default int parseInt(Node node)
{
return parseInt(node, -1);
}
/**
* Parses an integer value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Integer parseInteger(Node node, Integer defaultValue)
{
return node != null ? Integer.decode(node.getNodeValue()) : defaultValue;
}
/**
* Parses an integer value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Integer parseInteger(Node node)
{
return parseInteger(node, null);
}
/**
* Parses an integer value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Integer parseInteger(NamedNodeMap attrs, String name)
{
return parseInteger(attrs.getNamedItem(name));
}
/**
* Parses an integer value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Integer parseInteger(NamedNodeMap attrs, String name, Integer defaultValue)
{
return parseInteger(attrs.getNamedItem(name), defaultValue);
}
/**
* Parses a long value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Long parseLong(Node node, Long defaultValue)
{
return node != null ? Long.decode(node.getNodeValue()) : defaultValue;
}
/**
* Parses a long value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Long parseLong(Node node)
{
return parseLong(node, null);
}
/**
* Parses a long value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Long parseLong(NamedNodeMap attrs, String name)
{
return parseLong(attrs.getNamedItem(name));
}
/**
* Parses a long value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Long parseLong(NamedNodeMap attrs, String name, Long defaultValue)
{
return parseLong(attrs.getNamedItem(name), defaultValue);
}
/**
* Parses a float value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Float parseFloat(Node node, Float defaultValue)
{
return node != null ? Float.valueOf(node.getNodeValue()) : defaultValue;
}
/**
* Parses a float value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Float parseFloat(Node node)
{
return parseFloat(node, null);
}
/**
* Parses a float value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Float parseFloat(NamedNodeMap attrs, String name)
{
return parseFloat(attrs.getNamedItem(name));
}
/**
* Parses a float value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Float parseFloat(NamedNodeMap attrs, String name, Float defaultValue)
{
return parseFloat(attrs.getNamedItem(name), defaultValue);
}
/**
* Parses a double value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Double parseDouble(Node node, Double defaultValue)
{
return node != null ? Double.valueOf(node.getNodeValue()) : defaultValue;
}
/**
* Parses a double value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Double parseDouble(Node node)
{
return parseDouble(node, null);
}
/**
* Parses a double value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default Double parseDouble(NamedNodeMap attrs, String name)
{
return parseDouble(attrs.getNamedItem(name));
}
/**
* Parses a double value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default Double parseDouble(NamedNodeMap attrs, String name, Double defaultValue)
{
return parseDouble(attrs.getNamedItem(name), defaultValue);
}
/**
* Parses a string value.
* @param node the node to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default String parseString(Node node, String defaultValue)
{
return node != null ? node.getNodeValue() : defaultValue;
}
/**
* Parses a string value.
* @param node the node to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default String parseString(Node node)
{
return parseString(node, null);
}
/**
* Parses a string value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @return if the node is not null, the value of the parsed node, otherwise null
*/
default String parseString(NamedNodeMap attrs, String name)
{
return parseString(attrs.getNamedItem(name));
}
/**
* Parses a string value.
* @param attrs the attributes
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null, the value of the parsed node, otherwise the default value
*/
default String parseString(NamedNodeMap attrs, String name, String defaultValue)
{
return parseString(attrs.getNamedItem(name), defaultValue);
}
/**
* Parses an enumerated value.
* @param <T> the enumerated type
* @param node the node to parse
* @param clazz the class of the enumerated
* @param defaultValue the default value
* @return if the node is not null and the node value is valid the parsed value, otherwise the default value
*/
default <T extends Enum<T>> T parseEnum(Node node, Class<T> clazz, T defaultValue)
{
if (node == null)
{
return defaultValue;
}
try
{
return Enum.valueOf(clazz, node.getNodeValue());
}
catch (IllegalArgumentException e)
{
LOGGER.warning("Invalid value specified for node: " + node.getNodeName() + " specified value: " + node.getNodeValue() + " should be enum value of \"" + clazz.getSimpleName() + "\" using default value: " + defaultValue);
return defaultValue;
}
}
/**
* Parses an enumerated value.
* @param <T> the enumerated type
* @param node the node to parse
* @param clazz the class of the enumerated
* @return if the node is not null and the node value is valid the parsed value, otherwise null
*/
default <T extends Enum<T>> T parseEnum(Node node, Class<T> clazz)
{
return parseEnum(node, clazz, null);
}
/**
* Parses an enumerated value.
* @param <T> the enumerated type
* @param attrs the attributes
* @param clazz the class of the enumerated
* @param name the name of the attribute to parse
* @return if the node is not null and the node value is valid the parsed value, otherwise null
*/
default <T extends Enum<T>> T parseEnum(NamedNodeMap attrs, Class<T> clazz, String name)
{
return parseEnum(attrs.getNamedItem(name), clazz);
}
/**
* Parses an enumerated value.
* @param <T> the enumerated type
* @param attrs the attributes
* @param clazz the class of the enumerated
* @param name the name of the attribute to parse
* @param defaultValue the default value
* @return if the node is not null and the node value is valid the parsed value, otherwise the default value
*/
default <T extends Enum<T>> T parseEnum(NamedNodeMap attrs, Class<T> clazz, String name, T defaultValue)
{
return parseEnum(attrs.getNamedItem(name), clazz, defaultValue);
}
/**
* @param node
* @return parses all attributes to a Map
*/
default Map<String, Object> parseAttributes(Node node)
{
final NamedNodeMap attrs = node.getAttributes();
final Map<String, Object> map = new LinkedHashMap<>();
for (int i = 0; i < attrs.getLength(); i++)
{
final Node att = attrs.item(i);
map.put(att.getNodeName(), att.getNodeValue());
}
return map;
}
/**
* Executes action for each child of node
* @param node
* @param action
*/
default void forEach(Node node, Consumer<Node> action)
{
forEach(node, a -> true, action);
}
/**
* Executes action for each child that matches nodeName
* @param node
* @param nodeName
* @param action
*/
default void forEach(Node node, String nodeName, Consumer<Node> action)
{
forEach(node, innerNode -> nodeName.equalsIgnoreCase(innerNode.getNodeName()), action);
}
/**
* Executes action for each child of node if matches the filter specified
* @param node
* @param filter
* @param action
*/
default void forEach(Node node, Predicate<Node> filter, Consumer<Node> action)
{
final NodeList list = node.getChildNodes();
for (int i = 0; i < list.getLength(); i++)
{
final Node targetNode = list.item(i);
if (filter.test(targetNode))
{
action.accept(targetNode);
}
}
}
/**
* @param node
* @return {@code true} if the node is an element type, {@code false} otherwise
*/
static boolean isNode(Node node)
{
return node.getNodeType() == Node.ELEMENT_NODE;
}
/**
* @param node
* @return {@code true} if the node is an element type, {@code false} otherwise
*/
static boolean isText(Node node)
{
return node.getNodeType() == Node.TEXT_NODE;
}
/**
* Gets the current file filter.
* @return the current file filter
*/
default FileFilter getCurrentFileFilter()
{
return XML_FILTER;
}
/**
* Simple XML error handler.
* @author Zoey76
*/
class XMLErrorHandler implements ErrorHandler
{
@Override
public void warning(SAXParseException e) throws SAXParseException
{
throw e;
}
@Override
public void error(SAXParseException e) throws SAXParseException
{
throw e;
}
@Override
public void fatalError(SAXParseException e) throws SAXParseException
{
throw e;
}
}
}

View File

@ -0,0 +1,161 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import javax.swing.SwingUtilities;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
import javax.swing.text.Element;
/*
* A class to control the maximum number of lines to be stored in a Document
*
* Excess lines can be removed from the start or end of the Document
* depending on your requirement.
*
* a) if you append text to the Document, then you would want to remove lines
* from the start.
* b) if you insert text at the beginning of the Document, then you would
* want to remove lines from the end.
*/
public class LimitLinesDocumentListener implements DocumentListener
{
private int _maximumLines;
private final boolean _isRemoveFromStart;
/*
* Specify the number of lines to be stored in the Document. Extra lines will be removed from the start of the Document.
*/
public LimitLinesDocumentListener(int maximumLines)
{
this(maximumLines, true);
}
/*
* Specify the number of lines to be stored in the Document. Extra lines will be removed from the start or end of the Document, depending on the boolean value specified.
*/
public LimitLinesDocumentListener(int maximumLines, boolean isRemoveFromStart)
{
setLimitLines(maximumLines);
_isRemoveFromStart = isRemoveFromStart;
}
/*
* Return the maximum number of lines to be stored in the Document.
*/
public int getLimitLines()
{
return _maximumLines;
}
/*
* Set the maximum number of lines to be stored in the Document.
*/
public void setLimitLines(int maximumLines)
{
if (maximumLines < 1)
{
String message = "Maximum lines must be greater than 0";
throw new IllegalArgumentException(message);
}
_maximumLines = maximumLines;
}
/*
* Handle insertion of new text into the Document.
*/
@Override
public void insertUpdate(final DocumentEvent e)
{
// Changes to the Document can not be done within the listener so we need to add the processing to the end of the EDT.
SwingUtilities.invokeLater(() -> removeLines(e));
}
@Override
public void removeUpdate(DocumentEvent e)
{
}
@Override
public void changedUpdate(DocumentEvent e)
{
}
/*
* Remove lines from the Document when necessary.
*/
private void removeLines(DocumentEvent e)
{
// The root Element of the Document will tell us the total number of line in the Document.
Document document = e.getDocument();
Element root = document.getDefaultRootElement();
while (root.getElementCount() > _maximumLines)
{
if (_isRemoveFromStart)
{
removeFromStart(document, root);
}
else
{
removeFromEnd(document, root);
}
}
}
/*
* Remove lines from the start of the Document
*/
private void removeFromStart(Document document, Element root)
{
Element line = root.getElement(0);
int end = line.getEndOffset();
try
{
document.remove(0, end);
}
catch (BadLocationException ble)
{
System.out.println(ble);
}
}
/*
* Remove lines from the end of the Document
*/
private void removeFromEnd(Document document, Element root)
{
// We use start minus 1 to make sure we remove the newline character of the previous line.
Element line = root.getElement(root.getElementCount() - 1);
int start = line.getStartOffset();
int end = line.getEndOffset();
try
{
document.remove(start - 1, end - start);
}
catch (BadLocationException ble)
{
System.out.println(ble);
}
}
}

View File

@ -0,0 +1,381 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.nio.charset.Charset;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Properties;
import java.util.logging.Logger;
/**
* Simplifies loading of property files and adds logging if a non existing property is requested.
* @author NosBit
*/
public final class PropertiesParser
{
private static final Logger LOGGER = Logger.getLogger(PropertiesParser.class.getName());
private final Properties _properties = new Properties();
private final File _file;
public PropertiesParser(String name)
{
this(new File(name));
}
public PropertiesParser(File file)
{
_file = file;
try (FileInputStream fileInputStream = new FileInputStream(file))
{
try (InputStreamReader inputStreamReader = new InputStreamReader(fileInputStream, Charset.defaultCharset()))
{
_properties.load(inputStreamReader);
}
}
catch (Exception e)
{
LOGGER.warning("[" + _file.getName() + "] There was an error loading config reason: " + e.getMessage());
}
}
public boolean containskey(String key)
{
return _properties.containsKey(key);
}
private String getValue(String key)
{
final String value = _properties.getProperty(key);
return value != null ? value.trim() : null;
}
public boolean getBoolean(String key, boolean defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
if (value.equalsIgnoreCase("true"))
{
return true;
}
else if (value.equalsIgnoreCase("false"))
{
return false;
}
else
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be \"boolean\" using default value: " + defaultValue);
return defaultValue;
}
}
public byte getByte(String key, byte defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
try
{
return Byte.parseByte(value);
}
catch (NumberFormatException e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be \"byte\" using default value: " + defaultValue);
return defaultValue;
}
}
public short getShort(String key, short defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
try
{
return Short.parseShort(value);
}
catch (NumberFormatException e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be \"short\" using default value: " + defaultValue);
return defaultValue;
}
}
public int getInt(String key, int defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
try
{
return Integer.parseInt(value);
}
catch (NumberFormatException e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be \"int\" using default value: " + defaultValue);
return defaultValue;
}
}
public long getLong(String key, long defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
try
{
return Long.parseLong(value);
}
catch (NumberFormatException e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be \"long\" using default value: " + defaultValue);
return defaultValue;
}
}
public float getFloat(String key, float defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
try
{
return Float.parseFloat(value);
}
catch (NumberFormatException e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be \"float\" using default value: " + defaultValue);
return defaultValue;
}
}
public double getDouble(String key, double defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
try
{
return Double.parseDouble(value);
}
catch (NumberFormatException e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be \"double\" using default value: " + defaultValue);
return defaultValue;
}
}
public String getString(String key, String defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
return value;
}
public <T extends Enum<T>> T getEnum(String key, Class<T> clazz, T defaultValue)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValue);
return defaultValue;
}
try
{
return Enum.valueOf(clazz, value);
}
catch (IllegalArgumentException e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be enum value of \"" + clazz.getSimpleName() + "\" using default value: " + defaultValue);
return defaultValue;
}
}
/**
* @param durationPattern
* @param defaultValue
* @return {@link Duration} object by the durationPattern specified, {@code null} in case of malformed pattern.
*/
public Duration getDuration(String durationPattern, String defaultValue)
{
return getDuration(durationPattern, defaultValue, null);
}
/**
* @param durationPattern
* @param defaultValue
* @param defaultDuration
* @return {@link Duration} object by the durationPattern specified, the defaultDuration in case of malformed pattern.
*/
public Duration getDuration(String durationPattern, String defaultValue, Duration defaultDuration)
{
final String value = getString(durationPattern, defaultValue);
try
{
return TimeUtil.parseDuration(value);
}
catch (IllegalStateException e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + durationPattern + " specified value: " + value + " should be time patttern using default value: " + defaultValue);
}
return defaultDuration;
}
/**
* @param key
* @param separator
* @param defaultValues
* @return int array
*/
public int[] getIntArray(String key, String separator, int... defaultValues)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValues);
return defaultValues;
}
try
{
final String[] data = value.trim().split(separator);
final int[] result = new int[data.length];
for (int i = 0; i < data.length; i++)
{
result[i] = Integer.decode(data[i].trim());
}
return result;
}
catch (Exception e)
{
LOGGER.warning("[+_file.getName()+] Invalid value specified for key: " + key + " specified value: " + value + " should be array using default value: " + defaultValues);
return defaultValues;
}
}
/**
* @param <T>
* @param key
* @param separator
* @param clazz
* @param defaultValues
* @return enum array
*/
@SafeVarargs
public final <T extends Enum<T>> T[] getEnumArray(String key, String separator, Class<T> clazz, T... defaultValues)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValues);
return defaultValues;
}
try
{
final String[] data = value.trim().split(separator);
@SuppressWarnings("unchecked")
final T[] result = (T[]) Array.newInstance(clazz, data.length);
for (int i = 0; i < data.length; i++)
{
result[i] = Enum.valueOf(clazz, data[i]);
}
return result;
}
catch (Exception e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be array using default value: " + defaultValues);
return defaultValues;
}
}
/**
* @param <T>
* @param key
* @param separator
* @param clazz
* @param defaultValues
* @return list
*/
@SafeVarargs
public final <T extends Enum<T>> List<T> getEnumList(String key, String separator, Class<T> clazz, T... defaultValues)
{
final String value = getValue(key);
if (value == null)
{
LOGGER.warning("[" + _file.getName() + "] missing property for key: " + key + " using default value: " + defaultValues);
return Arrays.asList(defaultValues);
}
try
{
final String[] data = value.trim().split(separator);
final List<T> result = new ArrayList<>(data.length);
for (String element : data)
{
result.add(Enum.valueOf(clazz, element));
}
return result;
}
catch (Exception e)
{
LOGGER.warning("[" + _file.getName() + "] Invalid value specified for key: " + key + " specified value: " + value + " should be array using default value: " + defaultValues);
return Arrays.asList(defaultValues);
}
}
}

View File

@ -0,0 +1,457 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.security.SecureRandom;
import java.util.Random;
/**
* @author Forsaiken
*/
public final class Rnd
{
/**
* This class extends {@link java.util.Random} but do not compare and store atomically.<br>
* Instead it`s using a simple volatile flag to ensure reading and storing the whole 64bit seed chunk.<br>
* This implementation is much faster on parallel access, but may generate the same seed for 2 threads.
* @author Forsaiken
* @see java.util.Random
*/
public static final class NonAtomicRandom extends Random
{
private volatile long _seed;
public NonAtomicRandom()
{
this(++SEED_UNIQUIFIER + System.nanoTime());
}
public NonAtomicRandom(long seed)
{
setSeed(seed);
}
@Override
public final int next(int bits)
{
return (int) ((_seed = ((_seed * MULTIPLIER) + ADDEND) & MASK) >>> (48 - bits));
}
@Override
public final void setSeed(long seed)
{
_seed = (seed ^ MULTIPLIER) & MASK;
}
}
/**
* @author Forsaiken
*/
protected static final class RandomContainer
{
private final Random _random;
protected RandomContainer(Random random)
{
_random = random;
}
public final Random directRandom()
{
return _random;
}
/**
* Get a random double number from 0 to 1
* @return A random double number from 0 to 1
* @see com.l2jmobius.commons.util.Rnd#nextDouble()
*/
public final double get()
{
return _random.nextDouble();
}
/**
* Gets a random integer number from 0(inclusive) to n(exclusive)
* @param n The superior limit (exclusive)
* @return A random integer number from 0 to n-1
*/
public final int get(int n)
{
return (int) (_random.nextDouble() * n);
}
/**
* Gets a random integer number from min(inclusive) to max(inclusive)
* @param min The minimum value
* @param max The maximum value
* @return A random integer number from min to max
*/
public final int get(int min, int max)
{
return min + (int) (_random.nextDouble() * ((max - min) + 1));
}
/**
* Gets a random long number from min(inclusive) to max(inclusive)
* @param min The minimum value
* @param max The maximum value
* @return A random long number from min to max
*/
public final long get(long min, long max)
{
return min + (long) (_random.nextDouble() * ((max - min) + 1));
}
/**
* Get a random boolean state (true or false)
* @return A random boolean state (true or false)
* @see java.util.Random#nextBoolean()
*/
public final boolean nextBoolean()
{
return _random.nextBoolean();
}
/**
* Fill the given array with random byte numbers from Byte.MIN_VALUE(inclusive) to Byte.MAX_VALUE(inclusive)
* @param array The array to be filled with random byte numbers
* @see java.util.Random#nextBytes(byte[] bytes)
*/
public final void nextBytes(byte[] array)
{
_random.nextBytes(array);
}
/**
* Get a random double number from 0 to 1
* @return A random double number from 0 to 1
* @see java.util.Random#nextDouble()
*/
public final double nextDouble()
{
return _random.nextDouble();
}
/**
* Get a random float number from 0 to 1
* @return A random integer number from 0 to 1
* @see java.util.Random#nextFloat()
*/
public final float nextFloat()
{
return _random.nextFloat();
}
/**
* Get a random gaussian double number from 0 to 1
* @return A random gaussian double number from 0 to 1
* @see java.util.Random#nextGaussian()
*/
public final double nextGaussian()
{
return _random.nextGaussian();
}
/**
* Get a random integer number from Integer.MIN_VALUE(inclusive) to Integer.MAX_VALUE(inclusive)
* @return A random integer number from Integer.MIN_VALUE to Integer.MAX_VALUE
* @see java.util.Random#nextInt()
*/
public final int nextInt()
{
return _random.nextInt();
}
/**
* Get a random long number from Long.MIN_VALUE(inclusive) to Long.MAX_VALUE(inclusive)
* @return A random integer number from Long.MIN_VALUE to Long.MAX_VALUE
* @see java.util.Random#nextLong()
*/
public final long nextLong()
{
return _random.nextLong();
}
}
/**
* @author Forsaiken
*/
public enum RandomType
{
/**
* For best random quality.
* @see java.security.SecureRandom
*/
SECURE,
/**
* For average random quality.
* @see java.util.Random
*/
UNSECURE_ATOMIC,
/**
* Like {@link com.l2jmobius.commons.util.Rnd.RandomType#UNSECURE_ATOMIC}.<br>
* Each thread has it`s own random instance.<br>
* Provides best parallel access speed.
* @see com.l2jmobius.commons.util.Rnd.ThreadLocalRandom
*/
UNSECURE_THREAD_LOCAL,
/**
* Like {@link com.l2jmobius.commons.util.Rnd.RandomType#UNSECURE_ATOMIC}.<br>
* Provides much faster parallel access speed.
* @see com.l2jmobius.commons.util.Rnd.NonAtomicRandom
*/
UNSECURE_VOLATILE
}
/**
* This class extends {@link java.util.Random} but do not compare and store atomically.<br>
* Instead it`s using thread local ensure reading and storing the whole 64bit seed chunk.<br>
* This implementation is the fastest, never generates the same seed for 2 threads.<br>
* Each thread has it`s own random instance.
* @author Forsaiken
* @see java.util.Random
*/
public static final class ThreadLocalRandom extends Random
{
private static final class Seed
{
long _seed;
Seed(long seed)
{
setSeed(seed);
}
final int next(int bits)
{
return (int) ((_seed = ((_seed * MULTIPLIER) + ADDEND) & MASK) >>> (48 - bits));
}
final void setSeed(long seed)
{
_seed = (seed ^ MULTIPLIER) & MASK;
}
}
private final ThreadLocal<Seed> _seedLocal;
public ThreadLocalRandom()
{
_seedLocal = new ThreadLocal<>()
{
@Override
public final Seed initialValue()
{
return new Seed(++SEED_UNIQUIFIER + System.nanoTime());
}
};
}
public ThreadLocalRandom(long seed)
{
_seedLocal = new ThreadLocal<>()
{
@Override
public final Seed initialValue()
{
return new Seed(seed);
}
};
}
@Override
public final int next(int bits)
{
return _seedLocal.get().next(bits);
}
@Override
public final void setSeed(long seed)
{
if (_seedLocal != null)
{
_seedLocal.get().setSeed(seed);
}
}
}
private static final long ADDEND = 0xBL;
private static final long MASK = (1L << 48) - 1;
private static final long MULTIPLIER = 0x5DEECE66DL;
private static final RandomContainer rnd = newInstance(RandomType.UNSECURE_THREAD_LOCAL);
protected static volatile long SEED_UNIQUIFIER = 8682522807148012L;
public static Random directRandom()
{
return rnd.directRandom();
}
/**
* Get a random double number from 0 to 1
* @return A random double number from 0 to 1
* @see com.l2jmobius.commons.util.Rnd#nextDouble()
*/
public static double get()
{
return rnd.nextDouble();
}
/**
* Gets a random integer number from 0(inclusive) to n(exclusive)
* @param n The superior limit (exclusive)
* @return A random integer number from 0 to n-1
*/
public static int get(int n)
{
return rnd.get(n);
}
/**
* Gets a random integer number from min(inclusive) to max(inclusive)
* @param min The minimum value
* @param max The maximum value
* @return A random integer number from min to max
*/
public static int get(int min, int max)
{
return rnd.get(min, max);
}
/**
* Gets a random long number from min(inclusive) to max(inclusive)
* @param min The minimum value
* @param max The maximum value
* @return A random long number from min to max
*/
public static long get(long min, long max)
{
return rnd.get(min, max);
}
public static RandomContainer newInstance(RandomType type)
{
switch (type)
{
case UNSECURE_ATOMIC:
{
return new RandomContainer(new Random());
}
case UNSECURE_VOLATILE:
{
return new RandomContainer(new NonAtomicRandom());
}
case UNSECURE_THREAD_LOCAL:
{
return new RandomContainer(new ThreadLocalRandom());
}
case SECURE:
{
return new RandomContainer(new SecureRandom());
}
}
throw new IllegalArgumentException();
}
/**
* Get a random boolean state (true or false)
* @return A random boolean state (true or false)
* @see java.util.Random#nextBoolean()
*/
public static boolean nextBoolean()
{
return rnd.nextBoolean();
}
/**
* Fill the given array with random byte numbers from Byte.MIN_VALUE(inclusive) to Byte.MAX_VALUE(inclusive)
* @param array The array to be filled with random byte numbers
* @see java.util.Random#nextBytes(byte[] bytes)
*/
public static void nextBytes(byte[] array)
{
rnd.nextBytes(array);
}
/**
* Get a random double number from 0 to 1
* @return A random double number from 0 to 1
* @see java.util.Random#nextDouble()
*/
public static double nextDouble()
{
return rnd.nextDouble();
}
/**
* Get a random float number from 0 to 1
* @return A random integer number from 0 to 1
* @see java.util.Random#nextFloat()
*/
public static float nextFloat()
{
return rnd.nextFloat();
}
/**
* Get a random gaussian double number from 0 to 1
* @return A random gaussian double number from 0 to 1
* @see java.util.Random#nextGaussian()
*/
public static double nextGaussian()
{
return rnd.nextGaussian();
}
/**
* Get a random integer number from Integer.MIN_VALUE(inclusive) to Integer.MAX_VALUE(inclusive)
* @return A random integer number from Integer.MIN_VALUE to Integer.MAX_VALUE
* @see java.util.Random#nextInt()
*/
public static int nextInt()
{
return rnd.nextInt();
}
/**
* @param n
* @return int
* @see com.l2jmobius.commons.util.Rnd#get(int n)
*/
public static int nextInt(int n)
{
return get(n);
}
/**
* Get a random long number from Long.MIN_VALUE(inclusive) to Long.MAX_VALUE(inclusive)
* @return A random integer number from Long.MIN_VALUE to Long.MAX_VALUE
* @see java.util.Random#nextLong()
*/
public static long nextLong()
{
return rnd.nextLong();
}
}

View File

@ -0,0 +1,78 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Toolkit;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JWindow;
/**
* @author Mobius
*/
public class SplashScreen extends JWindow
{
Image image;
/**
* @param path of image file
* @param time in milliseconds
* @param parent frame to set visible after time ends
*/
public SplashScreen(String path, long time, JFrame parent)
{
setBackground(new Color(0, 255, 0, 0)); // Transparency.
image = Toolkit.getDefaultToolkit().getImage(path);
ImageIcon imageIcon = new ImageIcon(image);
setSize(imageIcon.getIconWidth(), imageIcon.getIconHeight());
setLocationRelativeTo(null);
setAlwaysOnTop(true);
setVisible(true);
new Timer().schedule(new TimerTask()
{
@Override
public void run()
{
setVisible(false);
if (parent != null)
{
// Make parent visible.
parent.setVisible(true);
// Focus parent window.
parent.toFront();
parent.setState(Frame.ICONIFIED);
parent.setState(Frame.NORMAL);
}
dispose();
}
}, imageIcon.getIconWidth() > 0 ? time : 100);
}
@Override
public void paint(Graphics g)
{
g.drawImage(image, 0, 0, null);
}
}

View File

@ -0,0 +1,276 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import com.l2jmobius.Config;
/**
* String utilities optimized for the best performance.<br>
* <h1>How to Use It</h1>
* <h2>concat() or append()</h2> If concatenating strings<br>
* in single call, use StringUtil.concat(), otherwise use StringUtil.append()<br>
* and its variants.<br>
* <br>
* <h2>Minimum Calls</h2><br>
* Bad:
*
* <pre>
* final StringBuilder sbString = new StringBuilder();
* StringUtil.append(sbString, &quot;text 1&quot;, String.valueOf(npcId));
* StringUtil.append(&quot;text 2&quot;);
* </pre>
*
* Good:
*
* <pre>
* final StringBuilder sbString = new StringBuilder();
* StringUtil.append(sbString, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
* </pre>
*
* Why?<br/>
* Because the less calls you do, the less memory re-allocations have to be done<br>
* so the whole text fits into the memory and less array copy tasks has to be<br>
* performed. So if using less calls, less memory is used and string concatenation is faster.<br>
* <br>
* <h2>Size Hints for Loops</h2><br>
* Bad:
*
* <pre>
* final StringBuilder sbString = new StringBuilder();
* StringUtil.append(sbString, &quot;header start&quot;, someText, &quot;header end&quot;);
* for (int i = 0; i &lt; 50; i++)
* {
* StringUtil.append(sbString, &quot;text 1&quot;, stringArray[i], &quot;text 2&quot;);
* }
* </pre>
*
* Good:
*
* <pre>
* final StringBuilder sbString = StringUtil.startAppend(1300, &quot;header start&quot;, someText, &quot;header end&quot;);
* for (int i = 0; i &lt; 50; i++)
* {
* StringUtil.append(sbString, &quot;text 1&quot;, stringArray[i], &quot;text 2&quot;);
* }
* </pre>
*
* Why?<br/>
* When using StringUtil.append(), memory is only allocated to fit in the strings in method argument. So on each loop new memory for the string has to be allocated and old string has to be copied to the new string. With size hint, even if the size hint is above the needed memory, memory is saved
* because new memory has not to be allocated on each cycle. Also it is much faster if no string copy tasks has to be performed. So if concatenating strings in a loop, count approximately the size and set it as the hint for the string builder size. It's better to make the size hint little bit larger
* rather than smaller.<br/>
* In case there is no text appended before the cycle, just use <code>new
* StringBuilder(1300)</code>.<br>
* <br>
* <h2>Concatenation and Constants</h2><br>
* Bad:
*
* <pre>
* StringUtil.concat(&quot;text 1 &quot;, &quot;text 2&quot;, String.valueOf(npcId));
* </pre>
*
* Good:
*
* <pre>
* StringUtil.concat(&quot;text 1 &quot; + &quot;text 2&quot;, String.valueOf(npcId));
* </pre>
*
* or
*
* <pre>
* StringUtil.concat(&quot;text 1 text 2&quot;, String.valueOf(npcId));
* </pre>
*
* Why?<br/>
* It saves some cycles when determining size of memory that needs to be allocated because less strings are passed to concat() method. But do not use + for concatenation of non-constant strings, that degrades performance and makes extra memory allocations needed.<br>
* <h2>Concatenation and Constant Variables</h2> Bad:
*
* <pre>
* String glue = &quot;some glue&quot;;
* StringUtil.concat(&quot;text 1&quot;, glue, &quot;text 2&quot;, glue, String.valueOf(npcId));
* </pre>
*
* Good:
*
* <pre>
* final String glue = &quot;some glue&quot;;
* StringUtil.concat(&quot;text 1&quot; + glue + &quot;text2&quot; + glue, String.valueOf(npcId));
* </pre>
*
* Why? Because when using <code>final</code> keyword, the <code>glue</code> is marked as constant string and compiler treats it as a constant string so it is able to create string "text1some gluetext2some glue" during the compilation. But this only works in case the value is known at compilation
* time, so this cannot be used for cases like <code>final String objectIdString =
* String.valueOf(getObjectId)</code>.<br>
* <br>
* <h2>StringBuilder Reuse</h2><br>
* Bad:
*
* <pre>
* final StringBuilder sbString1 = new StringBuilder();
* StringUtil.append(sbString1, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
* ... // output of sbString1, it is no more needed
* final StringBuilder sbString2 = new StringBuilder();
* StringUtil.append(sbString2, &quot;text 3&quot;, String.valueOf(npcId), &quot;text 4&quot;);
* </pre>
*
* Good:
*
* <pre>
* final StringBuilder sbString = new StringBuilder();
* StringUtil.append(sbString, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
* ... // output of sbString, it is no more needed
* sbString.setLength(0);
* StringUtil.append(sbString, &quot;text 3&quot;, String.valueOf(npcId), &quot;text 4&quot;);
* </pre>
*
* Why?</br>
* In first case, new memory has to be allocated for the second string. In second case already allocated memory is reused, but only in case the new string is not longer than the previously allocated string. Anyway, the second way is better because the string either fits in the memory and some memory
* is saved, or it does not fit in the memory, and in that case it works as in the first case.
* <h2>Primitives to Strings</h2> To convert primitives to string, use String.valueOf().<br>
* <br>
* <h2>How much faster is it?</h2><br>
* Here are some results of my tests. Count is number of strings concatenated. Don't take the numbers as 100% true as the numbers are affected by other programs running on my computer at the same time. Anyway, from the results it is obvious that using StringBuilder with predefined size is the
* fastest (and also most memory efficient) solution. It is about 5 times faster when concatenating 7 strings, compared to TextBuilder. Also, with more strings concatenated, the difference between StringBuilder and TextBuilder gets larger. In code, there are many cases, where there are concatenated
* 50+ strings so the time saving is even greater.<br>
*
* <pre>
* Count: 2
* TextBuilder: 1893
* TextBuilder with size: 1703
* String: 1033
* StringBuilder: 993
* StringBuilder with size: 1024
* Count: 3
* TextBuilder: 1973
* TextBuilder with size: 1872
* String: 2583
* StringBuilder: 1633
* StringBuilder with size: 1156
* Count: 4
* TextBuilder: 2188
* TextBuilder with size: 2229
* String: 4207
* StringBuilder: 1816
* StringBuilder with size: 1444
* Count: 5
* TextBuilder: 9185
* TextBuilder with size: 9464
* String: 6937
* StringBuilder: 2745
* StringBuilder with size: 1882
* Count: 6
* TextBuilder: 9785
* TextBuilder with size: 10082
* String: 9471
* StringBuilder: 2889
* StringBuilder with size: 1857
* Count: 7
* TextBuilder: 10169
* TextBuilder with size: 10528
* String: 12746
* StringBuilder: 3081
* StringBuilder with size: 2139
* </pre>
*
* @author fordfrog
*/
public final class StringUtil
{
private StringUtil()
{
}
/**
* Concatenates strings.
* @param strings strings to be concatenated
* @return concatenated string
*/
public static String concat(String... strings)
{
final StringBuilder sbString = new StringBuilder();
for (String string : strings)
{
sbString.append(string);
}
return sbString.toString();
}
/**
* Creates new string builder with size initializated to <code>sizeHint</code>, unless total length of strings is greater than <code>sizeHint</code>.
* @param sizeHint hint for string builder size allocation
* @param strings strings to be appended
* @return created string builder
*/
public static StringBuilder startAppend(int sizeHint, String... strings)
{
final int length = getLength(strings);
final StringBuilder sbString = new StringBuilder(sizeHint > length ? sizeHint : length);
for (String string : strings)
{
sbString.append(string);
}
return sbString;
}
/**
* Appends strings to existing string builder.
* @param sbString string builder
* @param strings strings to be appended
*/
public static void append(StringBuilder sbString, String... strings)
{
sbString.ensureCapacity(sbString.length() + getLength(strings));
for (String string : strings)
{
sbString.append(string);
}
}
public static int getLength(Iterable<String> strings)
{
int length = 0;
for (String string : strings)
{
length += (string == null) ? 4 : string.length();
}
return length;
}
/**
* Counts total length of all the strings.
* @param strings array of strings
* @return total length of all the strings
*/
public static int getLength(String[] strings)
{
int length = 0;
for (String string : strings)
{
length += (string == null) ? 4 : string.length();
}
return length;
}
public static String getTraceString(StackTraceElement[] trace)
{
final StringBuilder sbString = new StringBuilder();
for (StackTraceElement element : trace)
{
sbString.append(element.toString()).append(Config.EOL);
}
return sbString.toString();
}
}

View File

@ -0,0 +1,125 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util;
import java.time.Duration;
import java.time.temporal.ChronoUnit;
/**
* @author UnAfraid
*/
public class TimeUtil
{
private static int findIndexOfNonDigit(CharSequence text)
{
for (int i = 0; i < text.length(); i++)
{
if (Character.isDigit(text.charAt(i)))
{
continue;
}
return i;
}
return -1;
}
/**
* Parses patterns like:
* <ul>
* <li>1min or 10mins</li>
* <li>1day or 10days</li>
* <li>1week or 4weeks</li>
* <li>1month or 12months</li>
* <li>1year or 5years</li>
* </ul>
* @param datePattern
* @return {@link Duration} object converted by the date pattern specified.
* @throws IllegalStateException when malformed pattern specified.
*/
public static Duration parseDuration(String datePattern)
{
final int index = findIndexOfNonDigit(datePattern);
if (index == -1)
{
throw new IllegalStateException("Incorrect time format given: " + datePattern);
}
try
{
final int val = Integer.parseInt(datePattern.substring(0, index));
final String type = datePattern.substring(index);
final ChronoUnit unit;
switch (type.toLowerCase())
{
case "sec":
case "secs":
{
unit = ChronoUnit.SECONDS;
break;
}
case "min":
case "mins":
{
unit = ChronoUnit.MINUTES;
break;
}
case "hour":
case "hours":
{
unit = ChronoUnit.HOURS;
break;
}
case "day":
case "days":
{
unit = ChronoUnit.DAYS;
break;
}
case "week":
case "weeks":
{
unit = ChronoUnit.WEEKS;
break;
}
case "month":
case "months":
{
unit = ChronoUnit.MONTHS;
break;
}
case "year":
case "years":
{
unit = ChronoUnit.YEARS;
break;
}
default:
{
unit = ChronoUnit.valueOf(type);
if (unit == null)
{
throw new IllegalStateException("Incorrect format: " + type + " !!");
}
}
}
return Duration.of(val, unit);
}
catch (Exception e)
{
throw new IllegalStateException("Incorrect time format given: " + datePattern + " val: " + datePattern.substring(0, index));
}
}
}

View File

@ -0,0 +1,149 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util.crypt;
import javax.crypto.SecretKey;
import com.l2jmobius.commons.network.ICrypt;
import com.l2jmobius.commons.util.Rnd;
import io.netty.buffer.ByteBuf;
/**
* @author NosBit
*/
public class LoginCrypt implements ICrypt
{
private static final byte[] STATIC_BLOWFISH_KEY =
{
(byte) 0x6b,
(byte) 0x60,
(byte) 0xcb,
(byte) 0x5b,
(byte) 0x82,
(byte) 0xce,
(byte) 0x90,
(byte) 0xb1,
(byte) 0xcc,
(byte) 0x2b,
(byte) 0x6c,
(byte) 0x55,
(byte) 0x6c,
(byte) 0x6c,
(byte) 0x6c,
(byte) 0x6c
};
private static final BlowfishEngine STATIC_BLOWFISH_ENGINE = new BlowfishEngine();
static
{
STATIC_BLOWFISH_ENGINE.init(STATIC_BLOWFISH_KEY);
}
private final BlowfishEngine _blowfishEngine = new BlowfishEngine();
private boolean _static = true;
public LoginCrypt(SecretKey blowfishKey)
{
_blowfishEngine.init(blowfishKey.getEncoded());
}
/*
* (non-Javadoc)
* @see com.l2jserver.commons.network.ICrypt#encrypt(io.netty.buffer.ByteBuf)
*/
@Override
public void encrypt(ByteBuf buf)
{
// Checksum & XOR Key or Checksum only
buf.writeZero(_static ? 16 : 12);
// Padding
buf.writeZero(8 - (buf.readableBytes() % 8));
if (_static)
{
_static = false;
int key = Rnd.nextInt();
buf.skipBytes(4); // The first 4 bytes are ignored
while (buf.readerIndex() < (buf.writerIndex() - 8))
{
int data = buf.readIntLE();
key += data;
data ^= key;
buf.setIntLE(buf.readerIndex() - 4, data);
}
buf.setIntLE(buf.readerIndex(), key);
buf.resetReaderIndex();
final byte[] block = new byte[8];
while (buf.isReadable(8))
{
buf.readBytes(block);
STATIC_BLOWFISH_ENGINE.encryptBlock(block, 0);
buf.setBytes(buf.readerIndex() - block.length, block);
}
}
else
{
int checksum = 0;
while (buf.isReadable(8))
{
checksum ^= buf.readIntLE();
}
buf.setIntLE(buf.readerIndex(), checksum);
buf.resetReaderIndex();
final byte[] block = new byte[8];
while (buf.isReadable(8))
{
buf.readBytes(block);
_blowfishEngine.encryptBlock(block, 0);
buf.setBytes(buf.readerIndex() - block.length, block);
}
}
}
/*
* (non-Javadoc)
* @see com.l2jserver.commons.network.ICrypt#decrypt(io.netty.buffer.ByteBuf)
*/
@Override
public void decrypt(ByteBuf buf)
{
// Packet size must be multiple of 8
if ((buf.readableBytes() % 8) != 0)
{
buf.clear();
return;
}
final byte[] block = new byte[8];
while (buf.isReadable(8))
{
buf.readBytes(block);
_blowfishEngine.decryptBlock(block, 0);
buf.setBytes(buf.readerIndex() - block.length, block);
}
// TODO: verify checksum also dont forget!
}
}

View File

@ -0,0 +1,218 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util.crypt;
/**
* Class to use a blowfish cipher with ECB processing.<br>
* Static methods are present to append/check the checksum of<br>
* packets exchanged between the following partners:<br>
* Login Server <-> Game Client<br>
* Login Server <-> Game Server<br>
* Also a static method is provided for the initial xor encryption between Login Server <-> Game Client.
*/
public final class NewCrypt
{
private final BlowfishEngine _cipher;
/**
* @param blowfishKey
*/
public NewCrypt(byte[] blowfishKey)
{
_cipher = new BlowfishEngine();
_cipher.init(blowfishKey);
}
public NewCrypt(String key)
{
this(key.getBytes());
}
/**
* Equivalent to calling {@link #verifyChecksum(byte[], int, int)} with parameters (raw, 0, raw.length)
* @param raw data array to be verified
* @return true when the checksum of the data is valid, false otherwise
*/
public static boolean verifyChecksum(byte[] raw)
{
return verifyChecksum(raw, 0, raw.length);
}
/**
* Method to verify the checksum of a packet received by login server from game client.<br>
* This is also used for game server <-> login server communication.
* @param raw data array to be verified
* @param offset at which offset to start verifying
* @param size number of bytes to verify
* @return true if the checksum of the data is valid, false otherwise
*/
public static boolean verifyChecksum(byte[] raw, int offset, int size)
{
// check if size is multiple of 4 and if there is more then only the checksum
if (((size & 3) != 0) || (size <= 4))
{
return false;
}
long chksum = 0;
final int count = size - 4;
long check = -1;
int i;
for (i = offset; i < count; i += 4)
{
check = raw[i] & 0xff;
check |= (raw[i + 1] << 8) & 0xff00;
check |= (raw[i + 2] << 0x10) & 0xff0000;
check |= (raw[i + 3] << 0x18) & 0xff000000;
chksum ^= check;
}
check = raw[i] & 0xff;
check |= (raw[i + 1] << 8) & 0xff00;
check |= (raw[i + 2] << 0x10) & 0xff0000;
check |= (raw[i + 3] << 0x18) & 0xff000000;
return check == chksum;
}
/**
* Equivalent to calling {@link #appendChecksum(byte[], int, int)} with parameters (raw, 0, raw.length)
* @param raw data array to compute the checksum from
*/
public static void appendChecksum(byte[] raw)
{
appendChecksum(raw, 0, raw.length);
}
/**
* Method to append packet checksum at the end of the packet.
* @param raw data array to compute the checksum from
* @param offset offset where to start in the data array
* @param size number of bytes to compute the checksum from
*/
public static void appendChecksum(byte[] raw, int offset, int size)
{
long chksum = 0;
final int count = size - 4;
long ecx;
int i;
for (i = offset; i < count; i += 4)
{
ecx = raw[i] & 0xff;
ecx |= (raw[i + 1] << 8) & 0xff00;
ecx |= (raw[i + 2] << 0x10) & 0xff0000;
ecx |= (raw[i + 3] << 0x18) & 0xff000000;
chksum ^= ecx;
}
ecx = raw[i] & 0xff;
ecx |= (raw[i + 1] << 8) & 0xff00;
ecx |= (raw[i + 2] << 0x10) & 0xff0000;
ecx |= (raw[i + 3] << 0x18) & 0xff000000;
raw[i] = (byte) (chksum & 0xff);
raw[i + 1] = (byte) ((chksum >> 0x08) & 0xff);
raw[i + 2] = (byte) ((chksum >> 0x10) & 0xff);
raw[i + 3] = (byte) ((chksum >> 0x18) & 0xff);
}
/**
* Packet is first XOR encoded with <code>key</code> then, the last 4 bytes are overwritten with the the XOR "key".<br>
* Thus this assume that there is enough room for the key to fit without overwriting data.
* @param raw The raw bytes to be encrypted
* @param key The 4 bytes (int) XOR key
*/
public static void encXORPass(byte[] raw, int key)
{
encXORPass(raw, 0, raw.length, key);
}
/**
* Packet is first XOR encoded with <code>key</code> then, the last 4 bytes are overwritten with the the XOR "key".<br>
* Thus this assume that there is enough room for the key to fit without overwriting data.
* @param raw The raw bytes to be encrypted
* @param offset The beginning of the data to be encrypted
* @param size Length of the data to be encrypted
* @param key The 4 bytes (int) XOR key
*/
static void encXORPass(byte[] raw, int offset, int size, int key)
{
final int stop = size - 8;
int pos = 4 + offset;
int edx;
int ecx = key; // Initial xor key
while (pos < stop)
{
edx = raw[pos] & 0xFF;
edx |= (raw[pos + 1] & 0xFF) << 8;
edx |= (raw[pos + 2] & 0xFF) << 16;
edx |= (raw[pos + 3] & 0xFF) << 24;
ecx += edx;
edx ^= ecx;
raw[pos++] = (byte) (edx & 0xFF);
raw[pos++] = (byte) ((edx >> 8) & 0xFF);
raw[pos++] = (byte) ((edx >> 16) & 0xFF);
raw[pos++] = (byte) ((edx >> 24) & 0xFF);
}
raw[pos++] = (byte) (ecx & 0xFF);
raw[pos++] = (byte) ((ecx >> 8) & 0xFF);
raw[pos++] = (byte) ((ecx >> 16) & 0xFF);
raw[pos++] = (byte) ((ecx >> 24) & 0xFF);
}
/**
* Method to decrypt using Blowfish-Blockcipher in ECB mode.<br>
* The results will be directly placed inside {@code raw} array.<br>
* This method does not do any error checking, since the calling code<br>
* should ensure sizes.
* @param raw the data array to be decrypted
* @param offset the offset at which to start decrypting
* @param size the number of bytes to be decrypted
*/
public void decrypt(byte[] raw, int offset, int size)
{
for (int i = offset; i < (offset + size); i += 8)
{
_cipher.decryptBlock(raw, i);
}
}
/**
* Method to encrypt using Blowfish-Blockcipher in ECB mode.<br>
* The results will be directly placed inside {@code raw} array.<br>
* This method does not do any error checking, since the calling code should ensure sizes.
* @param raw the data array to be decrypted
* @param offset the offset at which to start decrypting
* @param size the number of bytes to be decrypted
*/
public void crypt(byte[] raw, int offset, int size)
{
for (int i = offset; i < (offset + size); i += 8)
{
_cipher.encryptBlock(raw, i);
}
}
}

View File

@ -0,0 +1,88 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util.crypt;
import java.math.BigInteger;
import java.security.Key;
import java.security.KeyPair;
import java.security.interfaces.RSAPublicKey;
import java.util.logging.Logger;
public class ScrambledKeyPair
{
private static Logger LOGGER = Logger.getLogger(ScrambledKeyPair.class.getName());
private final KeyPair _pair;
private final byte[] _scrambledModulus;
public ScrambledKeyPair(KeyPair pPair)
{
_pair = pPair;
_scrambledModulus = scrambleModulus(((RSAPublicKey) _pair.getPublic()).getModulus());
}
private byte[] scrambleModulus(BigInteger modulus)
{
byte[] scrambledMod = modulus.toByteArray();
if ((scrambledMod.length == 0x81) && (scrambledMod[0] == 0x00))
{
final byte[] temp = new byte[0x80];
System.arraycopy(scrambledMod, 1, temp, 0, 0x80);
scrambledMod = temp;
}
// step 1 : 0x4d-0x50 <-> 0x00-0x04
for (int i = 0; i < 4; i++)
{
final byte temp = scrambledMod[0x00 + i];
scrambledMod[0x00 + i] = scrambledMod[0x4d + i];
scrambledMod[0x4d + i] = temp;
}
// step 2 : xor first 0x40 bytes with last 0x40 bytes
for (int i = 0; i < 0x40; i++)
{
scrambledMod[i] = (byte) (scrambledMod[i] ^ scrambledMod[0x40 + i]);
}
// step 3 : xor bytes 0x0d-0x10 with bytes 0x34-0x38
for (int i = 0; i < 4; i++)
{
scrambledMod[0x0d + i] = (byte) (scrambledMod[0x0d + i] ^ scrambledMod[0x34 + i]);
}
// step 4 : xor last 0x40 bytes with first 0x40 bytes
for (int i = 0; i < 0x40; i++)
{
scrambledMod[0x40 + i] = (byte) (scrambledMod[0x40 + i] ^ scrambledMod[i]);
}
LOGGER.finer("Modulus was scrambled");
return scrambledMod;
}
public byte[] getScrambledModulus()
{
return _scrambledModulus;
}
public Key getPrivateKey()
{
return _pair.getPrivate();
}
public Key getPublicKey()
{
return _pair.getPublic();
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util.file.filter;
import java.io.File;
import java.io.FileFilter;
/**
* @author lasarus
*/
public class ExtFilter implements FileFilter
{
private final String _ext;
public ExtFilter(String ext)
{
_ext = ext;
}
@Override
public boolean accept(File f)
{
if ((f == null) || !f.isFile())
{
return false;
}
return f.getName().toLowerCase().endsWith(_ext);
}
}

View File

@ -0,0 +1,39 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util.file.filter;
import java.io.File;
import java.io.FileFilter;
/**
* Specialized {@link FileFilter} class.<br>
* Accepts <b>files</b> ending with ".htm" and ".html" only.
* @author Zoey76
*/
public class HTMLFilter implements FileFilter
{
@Override
public boolean accept(File f)
{
if ((f == null) || !f.isFile())
{
return false;
}
final String name = f.getName().toLowerCase();
return name.endsWith(".htm") || name.endsWith(".html");
}
}

View File

@ -0,0 +1,33 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util.file.filter;
import java.io.File;
/**
* Specialized {@link XMLFilter} class.<br>
* Accepts <b>files</b> matching "numbers".xml only.
* @author UnAfraid
*/
public class NumericNameFilter extends XMLFilter
{
@Override
public boolean accept(File f)
{
return super.accept(f) && f.getName().matches("\\d+\\.xml");
}
}

View File

@ -0,0 +1,30 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util.file.filter;
/**
* Specialized {@link ExtFilter} class.<br>
* Accepts <b>files</b> ending with ".sql" only.
* @author Zoey76
*/
public class SQLFilter extends ExtFilter
{
public SQLFilter()
{
super(".sql");
}
}

View File

@ -0,0 +1,30 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.commons.util.file.filter;
/**
* Specialized {@link ExtFilter} class.<br>
* Accepts files ending with ".xml" only.
* @author mrTJO
*/
public class XMLFilter extends ExtFilter
{
public XMLFilter()
{
super(".xml");
}
}