Interlude branch.
This commit is contained in:
@ -0,0 +1,299 @@
|
||||
/*
|
||||
* 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.ArrayList;
|
||||
import java.util.List;
|
||||
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 ThreadPoolManager
|
||||
{
|
||||
protected static final Logger LOGGER = Logger.getLogger(ThreadPoolManager.class.getName());
|
||||
|
||||
private static final long MAX_DELAY = TimeUnit.NANOSECONDS.toMillis(Long.MAX_VALUE - System.nanoTime()) / 2;
|
||||
|
||||
private static int _threadPoolRandomizer;
|
||||
|
||||
protected static ScheduledThreadPoolExecutor[] _scheduledPools;
|
||||
protected static ThreadPoolExecutor[] _instantPools;
|
||||
|
||||
/**
|
||||
* Init the different pools, based on Config. It is launched only once, on Gameserver instance.
|
||||
*/
|
||||
public static void init()
|
||||
{
|
||||
// Feed scheduled pool.
|
||||
int poolCount = Config.SCHEDULED_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
_scheduledPools = new ScheduledThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_scheduledPools[i] = new ScheduledThreadPoolExecutor(Config.THREADS_PER_SCHEDULED_THREAD_POOL);
|
||||
}
|
||||
|
||||
// Feed instant pool.
|
||||
poolCount = Config.INSTANT_THREAD_POOL_COUNT;
|
||||
if (poolCount == -1)
|
||||
{
|
||||
poolCount = Runtime.getRuntime().availableProcessors();
|
||||
}
|
||||
|
||||
_instantPools = new ThreadPoolExecutor[poolCount];
|
||||
for (int i = 0; i < poolCount; i++)
|
||||
{
|
||||
_instantPools[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 : _scheduledPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.prestartAllCoreThreads();
|
||||
}
|
||||
|
||||
// Launch purge task.
|
||||
scheduleAtFixedRate(() ->
|
||||
{
|
||||
purge();
|
||||
}, 600000, 600000);
|
||||
|
||||
LOGGER.info("ThreadPoolManager: Initialized " + getPoolSize(_instantPools) + "/" + getMaximumPoolSize(_instantPools) + " instant thread(s).");
|
||||
LOGGER.info("ThreadPoolManager: Initialized " + getPoolSize(_scheduledPools) + "/" + getMaximumPoolSize(_scheduledPools) + " scheduled thread(s).");
|
||||
}
|
||||
|
||||
public static void purge()
|
||||
{
|
||||
for (ScheduledThreadPoolExecutor threadPool1 : _scheduledPools)
|
||||
{
|
||||
threadPool1.purge();
|
||||
}
|
||||
for (ThreadPoolExecutor threadPool2 : _instantPools)
|
||||
{
|
||||
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(_scheduledPools).schedule(new TaskWrapper(r), validate(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(_scheduledPools).scheduleAtFixedRate(new TaskWrapper(r), validate(delay), validate(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(_instantPools).execute(new TaskWrapper(r));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
public static String[] getStats()
|
||||
{
|
||||
List<String> stats = new ArrayList<>();
|
||||
for (int i = 0; i < _scheduledPools.length; i++)
|
||||
{
|
||||
final ScheduledThreadPoolExecutor threadPool = _scheduledPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
for (int i = 0; i < _instantPools.length; i++)
|
||||
{
|
||||
final ThreadPoolExecutor threadPool = _instantPools[i];
|
||||
stats.add("Scheduled pool #" + i + ":");
|
||||
stats.add(" |- ActiveCount: ...... " + threadPool.getActiveCount());
|
||||
stats.add(" |- CorePoolSize: ..... " + threadPool.getCorePoolSize());
|
||||
stats.add(" |- PoolSize: ......... " + threadPool.getPoolSize());
|
||||
stats.add(" |- LargestPoolSize: .. " + threadPool.getLargestPoolSize());
|
||||
stats.add(" |- MaximumPoolSize: .. " + threadPool.getMaximumPoolSize());
|
||||
stats.add(" |- CompletedTaskCount: " + threadPool.getCompletedTaskCount());
|
||||
stats.add(" |- QueuedTaskCount: .. " + threadPool.getQueue().size());
|
||||
stats.add(" |- TaskCount: ........ " + threadPool.getTaskCount());
|
||||
stats.add(" | -------");
|
||||
}
|
||||
return stats.toArray(new String[stats.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shutdown thread pooling system correctly. Send different informations.
|
||||
*/
|
||||
public static void shutdown()
|
||||
{
|
||||
try
|
||||
{
|
||||
LOGGER.info("ThreadPoolManager: Shutting down.");
|
||||
|
||||
for (ScheduledThreadPoolExecutor threadPool : _scheduledPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
|
||||
for (ThreadPoolExecutor threadPool : _instantPools)
|
||||
{
|
||||
threadPool.shutdownNow();
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
t.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @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[_threadPoolRandomizer++ % threadPools.length];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param delay : The delay to validate.
|
||||
* @return a secured value, from 0 to MAX_DELAY.
|
||||
*/
|
||||
private static long validate(long delay)
|
||||
{
|
||||
return Math.max(0, Math.min(MAX_DELAY, delay));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall actual pools size.
|
||||
*/
|
||||
private static long getPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getPoolSize();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param threadPools : The pool array to check.
|
||||
* @return the overall maximum pools size.
|
||||
*/
|
||||
private static long getMaximumPoolSize(ThreadPoolExecutor[] threadPools)
|
||||
{
|
||||
long result = 0;
|
||||
|
||||
for (ThreadPoolExecutor threadPool : threadPools)
|
||||
{
|
||||
result += threadPool.getMaximumPoolSize();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public static final class TaskWrapper implements Runnable
|
||||
{
|
||||
private final Runnable _runnable;
|
||||
|
||||
public TaskWrapper(Runnable runnable)
|
||||
{
|
||||
_runnable = runnable;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
try
|
||||
{
|
||||
_runnable.run();
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
LOGGER.warning("Exception in " + _runnable.getClass().getName() + " execution: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,89 @@
|
||||
/*
|
||||
* 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.crypt;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import com.l2jmobius.commons.util.Rnd;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
*/
|
||||
public class LoginCrypt
|
||||
{
|
||||
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 final NewCrypt _staticCrypt = new NewCrypt(STATIC_BLOWFISH_KEY);
|
||||
private NewCrypt _crypt;
|
||||
private boolean _static = true;
|
||||
|
||||
public void setKey(byte[] key)
|
||||
{
|
||||
_crypt = new NewCrypt(key);
|
||||
}
|
||||
|
||||
public boolean decrypt(byte[] raw, int offset, int size) throws IOException
|
||||
{
|
||||
_crypt.decrypt(raw, offset, size);
|
||||
return NewCrypt.verifyChecksum(raw, offset, size);
|
||||
}
|
||||
|
||||
public int encrypt(byte[] raw, int offset, int size) throws IOException
|
||||
{
|
||||
// reserve checksum
|
||||
size += 4;
|
||||
|
||||
if (_static)
|
||||
{
|
||||
// reserve for XOR "key"
|
||||
size += 4;
|
||||
|
||||
// padding
|
||||
size += 8 - (size % 8);
|
||||
NewCrypt.encXORPass(raw, offset, size, Rnd.nextInt());
|
||||
_staticCrypt.crypt(raw, offset, size);
|
||||
|
||||
_static = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// padding
|
||||
size += 8 - (size % 8);
|
||||
NewCrypt.appendChecksum(raw, offset, size);
|
||||
_crypt.crypt(raw, offset, size);
|
||||
}
|
||||
return size;
|
||||
}
|
||||
}
|
@ -0,0 +1,215 @@
|
||||
/*
|
||||
* 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.crypt;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This class ...
|
||||
* @version $Revision: 1.3.4.1 $ $Date: 2005/03/27 15:30:09 $
|
||||
*/
|
||||
public class NewCrypt
|
||||
{
|
||||
protected static Logger LOGGER = Logger.getLogger(NewCrypt.class.getName());
|
||||
BlowfishEngine _crypt;
|
||||
BlowfishEngine _decrypt;
|
||||
|
||||
/**
|
||||
* @param blowfishKey
|
||||
*/
|
||||
public NewCrypt(byte[] blowfishKey)
|
||||
{
|
||||
_crypt = new BlowfishEngine();
|
||||
_crypt.init(true, blowfishKey);
|
||||
_decrypt = new BlowfishEngine();
|
||||
_decrypt.init(false, blowfishKey);
|
||||
}
|
||||
|
||||
public NewCrypt(String key)
|
||||
{
|
||||
this(key.getBytes());
|
||||
}
|
||||
|
||||
public static boolean verifyChecksum(byte[] raw)
|
||||
{
|
||||
return NewCrypt.verifyChecksum(raw, 0, raw.length);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
public static void appendChecksum(byte[] raw)
|
||||
{
|
||||
NewCrypt.appendChecksum(raw, 0, raw.length);
|
||||
}
|
||||
|
||||
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". 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)
|
||||
{
|
||||
NewCrypt.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". 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 begining of the data to be encrypted
|
||||
* @param size Length of the data to be encrypted
|
||||
* @param key The 4 bytes (int) XOR key
|
||||
*/
|
||||
public 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);
|
||||
}
|
||||
|
||||
public byte[] decrypt(byte[] raw) throws IOException
|
||||
{
|
||||
final byte[] result = new byte[raw.length];
|
||||
final int count = raw.length / 8;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_decrypt.processBlock(raw, i * 8, result, i * 8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void decrypt(byte[] raw, int offset, int size) throws IOException
|
||||
{
|
||||
final byte[] result = new byte[size];
|
||||
final int count = size / 8;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_decrypt.processBlock(raw, offset + (i * 8), result, i * 8);
|
||||
}
|
||||
// TODO can the crypt and decrypt go direct to the array
|
||||
System.arraycopy(result, 0, raw, offset, size);
|
||||
}
|
||||
|
||||
public byte[] crypt(byte[] raw) throws IOException
|
||||
{
|
||||
final int count = raw.length / 8;
|
||||
final byte[] result = new byte[raw.length];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_crypt.processBlock(raw, i * 8, result, i * 8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void crypt(byte[] raw, int offset, int size) throws IOException
|
||||
{
|
||||
final int count = size / 8;
|
||||
final byte[] result = new byte[size];
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
_crypt.processBlock(raw, offset + (i * 8), result, i * 8);
|
||||
}
|
||||
// TODO can the crypt and decrypt go direct to the array
|
||||
System.arraycopy(result, 0, raw, offset, size);
|
||||
}
|
||||
}
|
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.crypt;
|
||||
|
||||
import java.math.BigInteger;
|
||||
import java.security.KeyPair;
|
||||
import java.security.interfaces.RSAPublicKey;
|
||||
|
||||
public class ScrambledKeyPair
|
||||
{
|
||||
public KeyPair _pair;
|
||||
public 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))
|
||||
{
|
||||
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]);
|
||||
}
|
||||
|
||||
return scrambledMod;
|
||||
}
|
||||
}
|
@ -0,0 +1,230 @@
|
||||
/*
|
||||
* 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.crypt;
|
||||
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.concurrent.ScheduledFuture;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
|
||||
import com.l2jmobius.gameserver.network.L2GameClient;
|
||||
import com.l2jmobius.gameserver.network.serverpackets.GameGuardQuery;
|
||||
|
||||
/**
|
||||
* The main "engine" of protection ...
|
||||
* @author Nick
|
||||
*/
|
||||
public class nProtect
|
||||
{
|
||||
private static Logger LOGGER = Logger.getLogger(nProtect.class.getName());
|
||||
|
||||
public enum RestrictionType
|
||||
{
|
||||
RESTRICT_ENTER,
|
||||
RESTRICT_EVENT,
|
||||
RESTRICT_OLYMPIAD,
|
||||
RESTRICT_SIEGE
|
||||
}
|
||||
|
||||
public class nProtectAccessor
|
||||
{
|
||||
public nProtectAccessor()
|
||||
{
|
||||
}
|
||||
|
||||
public void setCheckGameGuardQuery(Method m)
|
||||
{
|
||||
_checkGameGuardQuery = m;
|
||||
}
|
||||
|
||||
public void setStartTask(Method m)
|
||||
{
|
||||
_startTask = m;
|
||||
}
|
||||
|
||||
public void setCheckRestriction(Method m)
|
||||
{
|
||||
_checkRestriction = m;
|
||||
}
|
||||
|
||||
public void setSendRequest(Method m)
|
||||
{
|
||||
_sendRequest = m;
|
||||
}
|
||||
|
||||
public void setCloseSession(Method m)
|
||||
{
|
||||
_closeSession = m;
|
||||
}
|
||||
|
||||
public void setSendGGQuery(Method m)
|
||||
{
|
||||
_sendGGQuery = m;
|
||||
}
|
||||
}
|
||||
|
||||
protected Method _checkGameGuardQuery = null;
|
||||
protected Method _startTask = null;
|
||||
protected Method _checkRestriction = null;
|
||||
protected Method _sendRequest = null;
|
||||
protected Method _closeSession = null;
|
||||
protected Method _sendGGQuery = null;
|
||||
private static nProtect _instance = null;
|
||||
|
||||
private static boolean enabled = false;
|
||||
|
||||
public static nProtect getInstance()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new nProtect();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
private nProtect()
|
||||
{
|
||||
Class<?> clazz = null;
|
||||
try
|
||||
{
|
||||
clazz = Class.forName("com.l2jmobius.protection.main");
|
||||
|
||||
if (clazz != null)
|
||||
{
|
||||
final Method m = clazz.getMethod("init", nProtectAccessor.class);
|
||||
if (m != null)
|
||||
{
|
||||
m.invoke(null, new nProtectAccessor());
|
||||
enabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (ClassNotFoundException e)
|
||||
{
|
||||
if (Config.DEBUG)
|
||||
{
|
||||
LOGGER.warning("nProtect System will be not loaded due to ClassNotFoundException of 'com.l2jmobius.protection.main' class");
|
||||
}
|
||||
}
|
||||
catch (SecurityException | InvocationTargetException | IllegalAccessException | IllegalArgumentException | NoSuchMethodException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public void sendGameGuardQuery(GameGuardQuery pkt)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_sendGGQuery != null)
|
||||
{
|
||||
_sendGGQuery.invoke(pkt);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkGameGuardRepy(L2GameClient cl, int[] reply)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_checkGameGuardQuery != null)
|
||||
{
|
||||
return (Boolean) _checkGameGuardQuery.invoke(null, cl, reply);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public ScheduledFuture<?> startTask(L2GameClient client)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_startTask != null)
|
||||
{
|
||||
return (ScheduledFuture<?>) _startTask.invoke(null, client);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void sendRequest(L2GameClient cl)
|
||||
{
|
||||
if (_sendRequest != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_sendRequest.invoke(null, cl);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void closeSession(L2GameClient cl)
|
||||
{
|
||||
if (_closeSession != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
_closeSession.invoke(null, cl);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean checkRestriction(L2PcInstance player, RestrictionType type, Object... params)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_checkRestriction != null)
|
||||
{
|
||||
return (Boolean) _checkRestriction.invoke(null, player, type, params);
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the enabled
|
||||
*/
|
||||
public static boolean isEnabled()
|
||||
{
|
||||
return enabled;
|
||||
}
|
||||
}
|
@ -0,0 +1,130 @@
|
||||
/*
|
||||
* 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.beans.PropertyVetoException;
|
||||
import java.sql.Connection;
|
||||
import java.sql.SQLException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.mchange.v2.c3p0.ComboPooledDataSource;
|
||||
|
||||
/**
|
||||
* Database Factory implementation.
|
||||
* @author Zoey76
|
||||
*/
|
||||
public class DatabaseFactory
|
||||
{
|
||||
private static final Logger _log = Logger.getLogger(DatabaseFactory.class.getName());
|
||||
|
||||
private final ComboPooledDataSource _dataSource;
|
||||
|
||||
public DatabaseFactory()
|
||||
{
|
||||
if (Config.DATABASE_MAX_CONNECTIONS < 2)
|
||||
{
|
||||
Config.DATABASE_MAX_CONNECTIONS = 2;
|
||||
_log.warning("A minimum of 2 connections are required.");
|
||||
}
|
||||
|
||||
_dataSource = new ComboPooledDataSource();
|
||||
_dataSource.setAutoCommitOnClose(true);
|
||||
|
||||
_dataSource.setInitialPoolSize(10);
|
||||
_dataSource.setMinPoolSize(10);
|
||||
_dataSource.setMaxPoolSize(Math.max(10, Config.DATABASE_MAX_CONNECTIONS));
|
||||
|
||||
_dataSource.setAcquireRetryAttempts(0); // try to obtain connections indefinitely (0 = never quit)
|
||||
_dataSource.setAcquireRetryDelay(500); // 500 milliseconds wait before try to acquire connection again
|
||||
_dataSource.setCheckoutTimeout(0); // 0 = wait indefinitely for new connection if pool is exhausted
|
||||
_dataSource.setAcquireIncrement(5); // if pool is exhausted, get 5 more connections at a time cause there is
|
||||
// a "long" delay on acquire connection so taking more than one connection at once will make connection pooling more effective.
|
||||
|
||||
// testing OnCheckin used with IdleConnectionTestPeriod is faster than testing on checkout
|
||||
_dataSource.setIdleConnectionTestPeriod(3600); // test idle connection every 60 sec
|
||||
_dataSource.setMaxIdleTime(Config.DATABASE_MAX_IDLE_TIME); // 0 = idle connections never expire
|
||||
// *THANKS* to connection testing configured above but I prefer to disconnect all connections not used for more than 1 hour
|
||||
|
||||
// enables statement caching, there is a "semi-bug" in c3p0 0.9.0 but in 0.9.0.2 and later it's fixed
|
||||
_dataSource.setMaxStatementsPerConnection(100);
|
||||
|
||||
_dataSource.setBreakAfterAcquireFailure(false); // never fail if any way possible setting this to true will make c3p0 "crash"
|
||||
// and refuse to work till restart thus making acquire errors "FATAL" ... we don't want that it should be possible to recover
|
||||
|
||||
try
|
||||
{
|
||||
_dataSource.setDriverClass(Config.DATABASE_DRIVER);
|
||||
}
|
||||
catch (PropertyVetoException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
_dataSource.setJdbcUrl(Config.DATABASE_URL);
|
||||
_dataSource.setUser(Config.DATABASE_LOGIN);
|
||||
_dataSource.setPassword(Config.DATABASE_PASSWORD);
|
||||
|
||||
/* Test the connection */
|
||||
try
|
||||
{
|
||||
_dataSource.getConnection().close();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public Connection getConnection()
|
||||
{
|
||||
Connection con = null;
|
||||
while (con == null)
|
||||
{
|
||||
try
|
||||
{
|
||||
con = _dataSource.getConnection();
|
||||
}
|
||||
catch (SQLException e)
|
||||
{
|
||||
_log.warning(getClass().getSimpleName() + ": Unable to get a connection: " + e.getMessage());
|
||||
}
|
||||
}
|
||||
return con;
|
||||
}
|
||||
|
||||
public void close()
|
||||
{
|
||||
try
|
||||
{
|
||||
_dataSource.close();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
_log.info(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
public static DatabaseFactory getInstance()
|
||||
{
|
||||
return SingletonHolder.INSTANCE;
|
||||
}
|
||||
|
||||
private static class SingletonHolder
|
||||
{
|
||||
protected static final DatabaseFactory INSTANCE = new DatabaseFactory();
|
||||
}
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class AbstractPacket<T extends MMOClient<?>>
|
||||
{
|
||||
protected ByteBuffer _buf;
|
||||
|
||||
T _client;
|
||||
|
||||
public final T getClient()
|
||||
{
|
||||
return _client;
|
||||
}
|
||||
}
|
@ -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.mmocore;
|
||||
|
||||
import java.nio.channels.SocketChannel;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
*/
|
||||
public interface IAcceptFilter
|
||||
{
|
||||
boolean accept(SocketChannel sc);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
* @param <T>
|
||||
*/
|
||||
public interface IClientFactory<T extends MMOClient<?>>
|
||||
{
|
||||
T create(MMOConnection<T> con);
|
||||
}
|
@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
* @param <T>
|
||||
*/
|
||||
public interface IMMOExecutor<T extends MMOClient<?>>
|
||||
{
|
||||
void execute(ReceivablePacket<T> packet);
|
||||
}
|
@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
* @param <T>
|
||||
*/
|
||||
public interface IPacketHandler<T extends MMOClient<?>>
|
||||
{
|
||||
ReceivablePacket<T> handlePacket(ByteBuffer buf, T client);
|
||||
}
|
@ -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.mmocore;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class MMOClient<T extends MMOConnection<?>>
|
||||
{
|
||||
private final T _con;
|
||||
|
||||
public MMOClient(T con)
|
||||
{
|
||||
_con = con;
|
||||
}
|
||||
|
||||
public T getConnection()
|
||||
{
|
||||
return _con;
|
||||
}
|
||||
|
||||
public abstract boolean decrypt(ByteBuffer buf, int size);
|
||||
|
||||
public abstract boolean encrypt(ByteBuffer buf, int size);
|
||||
|
||||
protected abstract void onDisconnection();
|
||||
|
||||
protected abstract void onForcedDisconnection(boolean critical);
|
||||
}
|
@ -0,0 +1,308 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.Socket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.CancelledKeyException;
|
||||
import java.nio.channels.ReadableByteChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.WritableByteChannel;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
* @param <T>
|
||||
*/
|
||||
public class MMOConnection<T extends MMOClient<?>>
|
||||
{
|
||||
private final SelectorThread<T> _selectorThread;
|
||||
|
||||
private final Socket _socket;
|
||||
|
||||
private final InputStream _socket_is;
|
||||
|
||||
private final InetAddress _address;
|
||||
|
||||
private final ReadableByteChannel _readableByteChannel;
|
||||
|
||||
private final WritableByteChannel _writableByteChannel;
|
||||
|
||||
private final int _port;
|
||||
|
||||
private final NioNetStackList<SendablePacket<T>> _sendQueue;
|
||||
|
||||
private final SelectionKey _selectionKey;
|
||||
|
||||
private ByteBuffer _readBuffer;
|
||||
|
||||
private ByteBuffer _primaryWriteBuffer;
|
||||
|
||||
private ByteBuffer _secondaryWriteBuffer;
|
||||
|
||||
private volatile boolean _pendingClose;
|
||||
|
||||
private T _client;
|
||||
|
||||
public MMOConnection(SelectorThread<T> selectorThread, Socket socket, SelectionKey key) throws IOException
|
||||
{
|
||||
_selectorThread = selectorThread;
|
||||
_socket = socket;
|
||||
_address = socket.getInetAddress();
|
||||
_readableByteChannel = socket.getChannel();
|
||||
_writableByteChannel = socket.getChannel();
|
||||
|
||||
_socket_is = socket.getInputStream();
|
||||
|
||||
_port = socket.getPort();
|
||||
_selectionKey = key;
|
||||
|
||||
_sendQueue = new NioNetStackList<>();
|
||||
}
|
||||
|
||||
final void setClient(T client)
|
||||
{
|
||||
_client = client;
|
||||
}
|
||||
|
||||
public final T getClient()
|
||||
{
|
||||
return _client;
|
||||
}
|
||||
|
||||
public final void sendPacket(SendablePacket<T> sp)
|
||||
{
|
||||
sp._client = _client;
|
||||
|
||||
if (_pendingClose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (getSendQueue())
|
||||
{
|
||||
_sendQueue.addLast(sp);
|
||||
}
|
||||
|
||||
if (!_sendQueue.isEmpty() && _selectionKey.isValid())
|
||||
{
|
||||
try
|
||||
{
|
||||
_selectionKey.interestOps(_selectionKey.interestOps() | SelectionKey.OP_WRITE);
|
||||
}
|
||||
catch (CancelledKeyException e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final SelectionKey getSelectionKey()
|
||||
{
|
||||
return _selectionKey;
|
||||
}
|
||||
|
||||
public final InetAddress getInetAddress()
|
||||
{
|
||||
return _address;
|
||||
}
|
||||
|
||||
public final int getPort()
|
||||
{
|
||||
return _port;
|
||||
}
|
||||
|
||||
final void close() throws IOException
|
||||
{
|
||||
_socket.close();
|
||||
}
|
||||
|
||||
final int read(ByteBuffer buf) throws IOException
|
||||
{
|
||||
return _readableByteChannel.read(buf);
|
||||
}
|
||||
|
||||
final int write(ByteBuffer buf) throws IOException
|
||||
{
|
||||
return _writableByteChannel.write(buf);
|
||||
}
|
||||
|
||||
final void createWriteBuffer(ByteBuffer buf)
|
||||
{
|
||||
if (_primaryWriteBuffer == null)
|
||||
{
|
||||
_primaryWriteBuffer = _selectorThread.getPooledBuffer();
|
||||
_primaryWriteBuffer.put(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
final ByteBuffer temp = _selectorThread.getPooledBuffer();
|
||||
temp.put(buf);
|
||||
|
||||
final int remaining = temp.remaining();
|
||||
_primaryWriteBuffer.flip();
|
||||
final int limit = _primaryWriteBuffer.limit();
|
||||
|
||||
if (remaining >= _primaryWriteBuffer.remaining())
|
||||
{
|
||||
temp.put(_primaryWriteBuffer);
|
||||
_selectorThread.recycleBuffer(_primaryWriteBuffer);
|
||||
}
|
||||
else
|
||||
{
|
||||
_primaryWriteBuffer.limit(remaining);
|
||||
temp.put(_primaryWriteBuffer);
|
||||
_primaryWriteBuffer.limit(limit);
|
||||
_primaryWriteBuffer.compact();
|
||||
_secondaryWriteBuffer = _primaryWriteBuffer;
|
||||
}
|
||||
_primaryWriteBuffer = temp;
|
||||
}
|
||||
}
|
||||
|
||||
final boolean hasPendingWriteBuffer()
|
||||
{
|
||||
return _primaryWriteBuffer != null;
|
||||
}
|
||||
|
||||
final void movePendingWriteBufferTo(ByteBuffer dest)
|
||||
{
|
||||
_primaryWriteBuffer.flip();
|
||||
dest.put(_primaryWriteBuffer);
|
||||
_selectorThread.recycleBuffer(_primaryWriteBuffer);
|
||||
_primaryWriteBuffer = _secondaryWriteBuffer;
|
||||
_secondaryWriteBuffer = null;
|
||||
}
|
||||
|
||||
final void setReadBuffer(ByteBuffer buf)
|
||||
{
|
||||
_readBuffer = buf;
|
||||
}
|
||||
|
||||
final ByteBuffer getReadBuffer()
|
||||
{
|
||||
return _readBuffer;
|
||||
}
|
||||
|
||||
public final boolean isConnected()
|
||||
{
|
||||
return !_socket.isClosed() && _socket.isConnected();
|
||||
}
|
||||
|
||||
public final boolean isChannelConnected()
|
||||
{
|
||||
boolean output = false;
|
||||
|
||||
if (!_socket.isClosed() && (_socket.getChannel() != null) && _socket.getChannel().isConnected() && _socket.getChannel().isOpen() && !_socket.isInputShutdown())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (_socket_is.available() > 0)
|
||||
{
|
||||
output = true;
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// not useful LOGGER
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
public final boolean isClosed()
|
||||
{
|
||||
return _pendingClose;
|
||||
}
|
||||
|
||||
final NioNetStackList<SendablePacket<T>> getSendQueue()
|
||||
{
|
||||
return _sendQueue;
|
||||
}
|
||||
|
||||
/*
|
||||
* final SendablePacket<T> getClosePacket() { return _closePacket; }
|
||||
*/
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public final void close(SendablePacket<T> sp)
|
||||
{
|
||||
close(new SendablePacket[]
|
||||
{
|
||||
sp
|
||||
});
|
||||
}
|
||||
|
||||
public final void close(SendablePacket<T>[] closeList)
|
||||
{
|
||||
if (_pendingClose)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (getSendQueue())
|
||||
{
|
||||
if (!_pendingClose)
|
||||
{
|
||||
_pendingClose = true;
|
||||
_sendQueue.clear();
|
||||
for (SendablePacket<T> sp : closeList)
|
||||
{
|
||||
_sendQueue.addLast(sp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (_selectionKey.isValid())
|
||||
{
|
||||
try
|
||||
{
|
||||
_selectionKey.interestOps(_selectionKey.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
}
|
||||
catch (CancelledKeyException e)
|
||||
{
|
||||
// not useful LOGGER
|
||||
}
|
||||
}
|
||||
|
||||
// _closePacket = sp;
|
||||
_selectorThread.closeConnection(this);
|
||||
}
|
||||
|
||||
final void releaseBuffers()
|
||||
{
|
||||
if (_primaryWriteBuffer != null)
|
||||
{
|
||||
_selectorThread.recycleBuffer(_primaryWriteBuffer);
|
||||
_primaryWriteBuffer = null;
|
||||
|
||||
if (_secondaryWriteBuffer != null)
|
||||
{
|
||||
_selectorThread.recycleBuffer(_secondaryWriteBuffer);
|
||||
_secondaryWriteBuffer = null;
|
||||
}
|
||||
}
|
||||
|
||||
if (_readBuffer != null)
|
||||
{
|
||||
_selectorThread.recycleBuffer(_readBuffer);
|
||||
_readBuffer = null;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,87 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.util.Properties;
|
||||
|
||||
public class NetcoreConfig
|
||||
{
|
||||
public boolean PACKET_HANDLER_DEBUG;
|
||||
|
||||
/** MMO settings */
|
||||
public int MMO_SELECTOR_SLEEP_TIME = 20; // default 20
|
||||
public int MMO_MAX_SEND_PER_PASS = 12; // default 12
|
||||
public int MMO_MAX_READ_PER_PASS = 12; // default 12
|
||||
public int MMO_HELPER_BUFFER_COUNT = 20; // default 20
|
||||
|
||||
/** Client Packets Queue settings */
|
||||
public int CLIENT_PACKET_QUEUE_SIZE; // default MMO_MAX_READ_PER_PASS + 2
|
||||
public int CLIENT_PACKET_QUEUE_MAX_BURST_SIZE; // default MMO_MAX_READ_PER_PASS + 1
|
||||
public int CLIENT_PACKET_QUEUE_MAX_PACKETS_PER_SECOND; // default 80
|
||||
public int CLIENT_PACKET_QUEUE_MEASURE_INTERVAL; // default 5
|
||||
public int CLIENT_PACKET_QUEUE_MAX_AVERAGE_PACKETS_PER_SECOND; // default 40
|
||||
public int CLIENT_PACKET_QUEUE_MAX_FLOODS_PER_MIN; // default 2
|
||||
public int CLIENT_PACKET_QUEUE_MAX_OVERFLOWS_PER_MIN; // default 1
|
||||
public int CLIENT_PACKET_QUEUE_MAX_UNDERFLOWS_PER_MIN; // default 1
|
||||
public int CLIENT_PACKET_QUEUE_MAX_UNKNOWN_PER_MIN; // default 5
|
||||
|
||||
// ============================================================
|
||||
private static NetcoreConfig _instance;
|
||||
|
||||
public static NetcoreConfig getInstance()
|
||||
{
|
||||
if (_instance == null)
|
||||
{
|
||||
_instance = new NetcoreConfig();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
private NetcoreConfig()
|
||||
{
|
||||
final String MMO_CONFIG = "./config/protected/mmocore.ini";
|
||||
|
||||
try
|
||||
{
|
||||
final Properties mmoSetting = new Properties();
|
||||
final InputStream is = new FileInputStream(new File(MMO_CONFIG));
|
||||
mmoSetting.load(is);
|
||||
is.close();
|
||||
|
||||
PACKET_HANDLER_DEBUG = Boolean.parseBoolean(mmoSetting.getProperty("PacketHandlerDebug", "False"));
|
||||
|
||||
// CLIENT-QUEUE-SETTINGS
|
||||
CLIENT_PACKET_QUEUE_SIZE = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueSize", "14")); // default MMO_MAX_READ_PER_PASS + 2
|
||||
CLIENT_PACKET_QUEUE_MAX_BURST_SIZE = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueMaxBurstSize", "13")); // default MMO_MAX_READ_PER_PASS + 1
|
||||
CLIENT_PACKET_QUEUE_MAX_PACKETS_PER_SECOND = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueMaxPacketsPerSecond", "80")); // default 80
|
||||
CLIENT_PACKET_QUEUE_MEASURE_INTERVAL = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueMeasureInterval", "5")); // default 5
|
||||
CLIENT_PACKET_QUEUE_MAX_AVERAGE_PACKETS_PER_SECOND = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueMaxAveragePacketsPerSecond", "40")); // default 40
|
||||
CLIENT_PACKET_QUEUE_MAX_FLOODS_PER_MIN = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueMaxFloodPerMin", "6")); // default 6
|
||||
CLIENT_PACKET_QUEUE_MAX_OVERFLOWS_PER_MIN = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueOverflowsPerMin", "3")); // default 3
|
||||
CLIENT_PACKET_QUEUE_MAX_UNDERFLOWS_PER_MIN = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueUnderflowsPerMin", "3")); // default 3
|
||||
CLIENT_PACKET_QUEUE_MAX_UNKNOWN_PER_MIN = Integer.parseInt(mmoSetting.getProperty("ClientPacketQueueUnknownPerMin", "5")); // default 5
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
throw new Error("Failed to Load " + MMO_CONFIG + " File.");
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
/**
|
||||
* @author Forsaiken
|
||||
* @param <E>
|
||||
*/
|
||||
public final class NioNetStackList<E>
|
||||
{
|
||||
private final NioNetStackNode _start = new NioNetStackNode();
|
||||
|
||||
private final NioNetStackNodeBuf _buf = new NioNetStackNodeBuf();
|
||||
|
||||
private NioNetStackNode _end = new NioNetStackNode();
|
||||
|
||||
public NioNetStackList()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
public final void addLast(E elem)
|
||||
{
|
||||
final NioNetStackNode newEndNode = _buf.removeFirst();
|
||||
_end._value = elem;
|
||||
_end._next = newEndNode;
|
||||
_end = newEndNode;
|
||||
}
|
||||
|
||||
public final E removeFirst()
|
||||
{
|
||||
final NioNetStackNode old = _start._next;
|
||||
final E value = old._value;
|
||||
_start._next = old._next;
|
||||
_buf.addLast(old);
|
||||
return value;
|
||||
}
|
||||
|
||||
public final boolean isEmpty()
|
||||
{
|
||||
return _start._next == _end;
|
||||
}
|
||||
|
||||
public final void clear()
|
||||
{
|
||||
_start._next = _end;
|
||||
}
|
||||
|
||||
protected final class NioNetStackNode
|
||||
{
|
||||
protected NioNetStackNode _next;
|
||||
|
||||
protected E _value;
|
||||
}
|
||||
|
||||
private final class NioNetStackNodeBuf
|
||||
{
|
||||
private final NioNetStackNode _start = new NioNetStackNode();
|
||||
|
||||
private NioNetStackNode _end = new NioNetStackNode();
|
||||
|
||||
NioNetStackNodeBuf()
|
||||
{
|
||||
_start._next = _end;
|
||||
}
|
||||
|
||||
final void addLast(NioNetStackNode node)
|
||||
{
|
||||
node._next = null;
|
||||
node._value = null;
|
||||
_end._next = node;
|
||||
_end = node;
|
||||
}
|
||||
|
||||
final NioNetStackNode removeFirst()
|
||||
{
|
||||
if (_start._next == _end)
|
||||
{
|
||||
return new NioNetStackNode();
|
||||
}
|
||||
|
||||
final NioNetStackNode old = _start._next;
|
||||
_start._next = old._next;
|
||||
return old;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
import java.nio.BufferOverflowException;
|
||||
|
||||
/**
|
||||
* @author Forsaiken
|
||||
*/
|
||||
public final class NioNetStringBuffer
|
||||
{
|
||||
private final char[] _buf;
|
||||
|
||||
private final int _size;
|
||||
|
||||
private int _len;
|
||||
|
||||
public NioNetStringBuffer(int size)
|
||||
{
|
||||
_buf = new char[size];
|
||||
_size = size;
|
||||
_len = 0;
|
||||
}
|
||||
|
||||
public final void clear()
|
||||
{
|
||||
_len = 0;
|
||||
}
|
||||
|
||||
public final void append(char c)
|
||||
{
|
||||
if (_len < _size)
|
||||
{
|
||||
_buf[_len++] = c;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new BufferOverflowException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final String toString()
|
||||
{
|
||||
return new String(_buf, 0, _len);
|
||||
}
|
||||
}
|
@ -0,0 +1,139 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class ReceivablePacket<T extends MMOClient<?>>extends AbstractPacket<T> implements Runnable
|
||||
{
|
||||
NioNetStringBuffer _sbuf;
|
||||
|
||||
protected ReceivablePacket()
|
||||
{
|
||||
}
|
||||
|
||||
protected abstract boolean read();
|
||||
|
||||
@Override
|
||||
public abstract void run();
|
||||
|
||||
/**
|
||||
* Reads <B>byte[]</B> from the buffer. <BR>
|
||||
* Reads as many bytes as the length of the array.
|
||||
* @param dst : the byte array which will be filled with the data.
|
||||
*/
|
||||
protected final void readB(byte[] dst)
|
||||
{
|
||||
_buf.get(dst);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <B>byte[]</B> from the buffer. <BR>
|
||||
* Reads as many bytes as the given length (len). Starts to fill the byte array from the given offset to <B>offset</B> + <B>len</B>.
|
||||
* @param dst : the byte array which will be filled with the data.
|
||||
* @param offset : starts to fill the byte array from the given offset.
|
||||
* @param len : the given length of bytes to be read.
|
||||
*/
|
||||
protected final void readB(byte[] dst, int offset, int len)
|
||||
{
|
||||
_buf.get(dst, offset, len);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <B>byte</B> from the buffer. <BR>
|
||||
* 8bit integer (00)
|
||||
* @return
|
||||
*/
|
||||
protected final int readC()
|
||||
{
|
||||
return _buf.get() & 0xFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <B>short</B> from the buffer. <BR>
|
||||
* 16bit integer (00 00)
|
||||
* @return
|
||||
*/
|
||||
protected final int readH()
|
||||
{
|
||||
return _buf.getShort() & 0xFFFF;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <B>int</B> from the buffer. <BR>
|
||||
* 32bit integer (00 00 00 00)
|
||||
* @return
|
||||
*/
|
||||
protected final int readD()
|
||||
{
|
||||
return _buf.getInt();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <B>long</B> from the buffer. <BR>
|
||||
* 64bit integer (00 00 00 00 00 00 00 00)
|
||||
* @return
|
||||
*/
|
||||
protected final long readQ()
|
||||
{
|
||||
return _buf.getLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <B>double</B> from the buffer. <BR>
|
||||
* 64bit double precision float (00 00 00 00 00 00 00 00)
|
||||
* @return
|
||||
*/
|
||||
protected final double readF()
|
||||
{
|
||||
return _buf.getDouble();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads <B>String</B> from the buffer.
|
||||
* @return
|
||||
*/
|
||||
protected final String readS()
|
||||
{
|
||||
_sbuf.clear();
|
||||
|
||||
char ch;
|
||||
while ((ch = _buf.getChar()) != 0)
|
||||
{
|
||||
_sbuf.append(ch);
|
||||
}
|
||||
|
||||
return _sbuf.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* packet forge purpose
|
||||
* @param data
|
||||
* @param client
|
||||
* @param sBuffer
|
||||
*/
|
||||
public void setBuffers(ByteBuffer data, T client, NioNetStringBuffer sBuffer)
|
||||
{
|
||||
_buf = data;
|
||||
_client = client;
|
||||
_sbuf = sBuffer;
|
||||
}
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
*/
|
||||
|
||||
public final class SelectorConfig
|
||||
{
|
||||
private int readBufferSize = 64 * 1024;
|
||||
|
||||
private int writeBufferSize = 64 * 1024;
|
||||
|
||||
private int helperBufferCount = 20;
|
||||
|
||||
private int helperBufferSize = 64 * 1024;
|
||||
|
||||
/**
|
||||
* Server will try to send maxSendPerPass packets per socket write call<br>
|
||||
* however it may send less if the write buffer was filled before achieving this value.
|
||||
*/
|
||||
private int maxSendPerPass = 10;
|
||||
|
||||
/**
|
||||
* Server will try to read maxReadPerPass packets per socket read call<br>
|
||||
* however it may read less if the read buffer was empty before achieving this value.
|
||||
*/
|
||||
private int maxReadPerPass = 10;
|
||||
|
||||
/**
|
||||
* Defines how much time (in milis) should the selector sleep, an higher value increases throughput but also increases latency(to a max of the sleep value itself).<BR>
|
||||
* Also an extremely high value(usually > 100) will decrease throughput due to the server not doing enough sends per second (depends on max sends per pass).<BR>
|
||||
* <BR>
|
||||
* Recommended values:<BR>
|
||||
* 1 for minimal latency.<BR>
|
||||
* 10-30 for an latency/troughput trade-off based on your needs.<BR>
|
||||
*/
|
||||
private int sleepTime = 10;
|
||||
|
||||
public int getReadBufferSize()
|
||||
{
|
||||
return readBufferSize;
|
||||
}
|
||||
|
||||
public void setReadBufferSize(int readBufferSize)
|
||||
{
|
||||
this.readBufferSize = readBufferSize;
|
||||
}
|
||||
|
||||
public int getWriteBufferSize()
|
||||
{
|
||||
return writeBufferSize;
|
||||
}
|
||||
|
||||
public void setWriteBufferSize(int writeBufferSize)
|
||||
{
|
||||
this.writeBufferSize = writeBufferSize;
|
||||
}
|
||||
|
||||
public int getHelperBufferCount()
|
||||
{
|
||||
return helperBufferCount;
|
||||
}
|
||||
|
||||
public void setHelperBufferCount(int helperBufferCount)
|
||||
{
|
||||
this.helperBufferCount = helperBufferCount;
|
||||
}
|
||||
|
||||
public int getHelperBufferSize()
|
||||
{
|
||||
return helperBufferSize;
|
||||
}
|
||||
|
||||
public void setHelperBufferSize(int helperBufferSize)
|
||||
{
|
||||
this.helperBufferSize = helperBufferSize;
|
||||
}
|
||||
|
||||
public int getMaxSendPerPass()
|
||||
{
|
||||
return maxSendPerPass;
|
||||
}
|
||||
|
||||
public void setMaxSendPerPass(int maxSendPerPass)
|
||||
{
|
||||
this.maxSendPerPass = maxSendPerPass;
|
||||
}
|
||||
|
||||
public int getMaxReadPerPass()
|
||||
{
|
||||
return maxReadPerPass;
|
||||
}
|
||||
|
||||
public void setMaxReadPerPass(int maxReadPerPass)
|
||||
{
|
||||
this.maxReadPerPass = maxReadPerPass;
|
||||
}
|
||||
|
||||
public int getSleepTime()
|
||||
{
|
||||
return sleepTime;
|
||||
}
|
||||
|
||||
public void setSleepTime(int sleepTime)
|
||||
{
|
||||
this.sleepTime = sleepTime;
|
||||
}
|
||||
}
|
@ -0,0 +1,729 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.ByteOrder;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.Selector;
|
||||
import java.nio.channels.ServerSocketChannel;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
|
||||
/**
|
||||
* Parts of design based on network core from WoodenGil
|
||||
* @param <T>
|
||||
* @author KenM
|
||||
*/
|
||||
public final class SelectorThread<T extends MMOClient<?>>extends Thread
|
||||
{
|
||||
// default BYTE_ORDER
|
||||
private static final ByteOrder BYTE_ORDER = ByteOrder.LITTLE_ENDIAN;
|
||||
// default HEADER_SIZE
|
||||
private static final int HEADER_SIZE = 2;
|
||||
// Selector
|
||||
private final Selector _selector;
|
||||
// Implementations
|
||||
private final IPacketHandler<T> _packetHandler;
|
||||
private final IMMOExecutor<T> _executor;
|
||||
private final IClientFactory<T> _clientFactory;
|
||||
private final IAcceptFilter _acceptFilter;
|
||||
// Configurations
|
||||
private final int HELPER_BUFFER_SIZE;
|
||||
private final int HELPER_BUFFER_COUNT;
|
||||
private final int MAX_SEND_PER_PASS;
|
||||
private final int MAX_READ_PER_PASS;
|
||||
private final long SLEEP_TIME;
|
||||
// Main Buffers
|
||||
private final ByteBuffer DIRECT_WRITE_BUFFER;
|
||||
private final ByteBuffer WRITE_BUFFER;
|
||||
private final ByteBuffer READ_BUFFER;
|
||||
// String Buffer
|
||||
private final NioNetStringBuffer STRING_BUFFER;
|
||||
// ByteBuffers General Purpose Pool
|
||||
private final LinkedList<ByteBuffer> _bufferPool;
|
||||
// Pending Close
|
||||
private final NioNetStackList<MMOConnection<T>> _pendingClose;
|
||||
|
||||
private boolean _shutdown;
|
||||
|
||||
public SelectorThread(SelectorConfig sc, IMMOExecutor<T> executor, IPacketHandler<T> packetHandler, IClientFactory<T> clientFactory, IAcceptFilter acceptFilter) throws IOException
|
||||
{
|
||||
super.setName("SelectorThread-" + super.getId());
|
||||
|
||||
HELPER_BUFFER_SIZE = sc.getHelperBufferSize();
|
||||
HELPER_BUFFER_COUNT = sc.getHelperBufferCount();
|
||||
MAX_SEND_PER_PASS = sc.getMaxSendPerPass();
|
||||
MAX_READ_PER_PASS = sc.getMaxReadPerPass();
|
||||
|
||||
SLEEP_TIME = sc.getSleepTime();
|
||||
|
||||
DIRECT_WRITE_BUFFER = ByteBuffer.allocateDirect(sc.getWriteBufferSize()).order(BYTE_ORDER);
|
||||
WRITE_BUFFER = ByteBuffer.wrap(new byte[sc.getWriteBufferSize()]).order(BYTE_ORDER);
|
||||
READ_BUFFER = ByteBuffer.wrap(new byte[sc.getReadBufferSize()]).order(BYTE_ORDER);
|
||||
|
||||
STRING_BUFFER = new NioNetStringBuffer(64 * 1024);
|
||||
|
||||
_pendingClose = new NioNetStackList<>();
|
||||
_bufferPool = new LinkedList<>();
|
||||
|
||||
for (int i = 0; i < HELPER_BUFFER_COUNT; i++)
|
||||
{
|
||||
_bufferPool.addLast(ByteBuffer.wrap(new byte[HELPER_BUFFER_SIZE]).order(BYTE_ORDER));
|
||||
}
|
||||
|
||||
_acceptFilter = acceptFilter;
|
||||
_packetHandler = packetHandler;
|
||||
_clientFactory = clientFactory;
|
||||
_executor = executor;
|
||||
_selector = Selector.open();
|
||||
}
|
||||
|
||||
public final void openServerSocket(InetAddress address, int tcpPort) throws IOException
|
||||
{
|
||||
final ServerSocketChannel selectable = ServerSocketChannel.open();
|
||||
selectable.configureBlocking(false);
|
||||
|
||||
final ServerSocket ss = selectable.socket();
|
||||
|
||||
if (address != null)
|
||||
{
|
||||
ss.bind(new InetSocketAddress(address, tcpPort));
|
||||
}
|
||||
else
|
||||
{
|
||||
ss.bind(new InetSocketAddress(tcpPort));
|
||||
}
|
||||
|
||||
selectable.register(_selector, SelectionKey.OP_ACCEPT);
|
||||
}
|
||||
|
||||
final ByteBuffer getPooledBuffer()
|
||||
{
|
||||
if (_bufferPool.isEmpty())
|
||||
{
|
||||
return ByteBuffer.wrap(new byte[HELPER_BUFFER_SIZE]).order(BYTE_ORDER);
|
||||
}
|
||||
|
||||
return _bufferPool.removeFirst();
|
||||
}
|
||||
|
||||
final void recycleBuffer(ByteBuffer buf)
|
||||
{
|
||||
if (_bufferPool.size() < HELPER_BUFFER_COUNT)
|
||||
{
|
||||
buf.clear();
|
||||
_bufferPool.addLast(buf);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final void run()
|
||||
{
|
||||
int selectedKeysCount = 0;
|
||||
|
||||
SelectionKey key;
|
||||
MMOConnection<T> con;
|
||||
|
||||
Iterator<SelectionKey> selectedKeys;
|
||||
|
||||
while (!_shutdown)
|
||||
{
|
||||
try
|
||||
{
|
||||
selectedKeysCount = _selector.selectNow();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (selectedKeysCount > 0)
|
||||
{
|
||||
selectedKeys = _selector.selectedKeys().iterator();
|
||||
|
||||
while (selectedKeys.hasNext())
|
||||
{
|
||||
key = selectedKeys.next();
|
||||
selectedKeys.remove();
|
||||
|
||||
con = (MMOConnection<T>) key.attachment();
|
||||
|
||||
switch (key.readyOps())
|
||||
{
|
||||
case SelectionKey.OP_CONNECT:
|
||||
{
|
||||
finishConnection(key, con);
|
||||
break;
|
||||
}
|
||||
case SelectionKey.OP_ACCEPT:
|
||||
{
|
||||
acceptConnection(key, con);
|
||||
break;
|
||||
}
|
||||
case SelectionKey.OP_READ:
|
||||
{
|
||||
readPacket(key, con);
|
||||
break;
|
||||
}
|
||||
case SelectionKey.OP_WRITE:
|
||||
{
|
||||
writePacket(key, con);
|
||||
break;
|
||||
}
|
||||
case SelectionKey.OP_READ | SelectionKey.OP_WRITE:
|
||||
{
|
||||
writePacket(key, con);
|
||||
if (key.isValid())
|
||||
{
|
||||
readPacket(key, con);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
synchronized (_pendingClose)
|
||||
{
|
||||
while (!_pendingClose.isEmpty())
|
||||
{
|
||||
try
|
||||
{
|
||||
con = _pendingClose.removeFirst();
|
||||
writeClosePacket(con);
|
||||
closeConnectionImpl(con.getSelectionKey(), con);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep(SLEEP_TIME);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
closeSelectorThread();
|
||||
}
|
||||
|
||||
private final void finishConnection(SelectionKey key, MMOConnection<T> con)
|
||||
{
|
||||
try
|
||||
{
|
||||
((SocketChannel) key.channel()).finishConnect();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
con.getClient().onForcedDisconnection(true);
|
||||
closeConnectionImpl(key, con);
|
||||
}
|
||||
|
||||
// key might have been invalidated on finishConnect()
|
||||
if (key.isValid())
|
||||
{
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_CONNECT);
|
||||
}
|
||||
}
|
||||
|
||||
private final void acceptConnection(SelectionKey key, MMOConnection<T> con)
|
||||
{
|
||||
final ServerSocketChannel ssc = (ServerSocketChannel) key.channel();
|
||||
SocketChannel sc;
|
||||
|
||||
try
|
||||
{
|
||||
while ((sc = ssc.accept()) != null)
|
||||
{
|
||||
if ((_acceptFilter == null) || _acceptFilter.accept(sc))
|
||||
{
|
||||
sc.configureBlocking(false);
|
||||
final SelectionKey clientKey = sc.register(_selector, SelectionKey.OP_READ);
|
||||
con = new MMOConnection<>(this, sc.socket(), clientKey);
|
||||
con.setClient(_clientFactory.create(con));
|
||||
clientKey.attach(con);
|
||||
}
|
||||
else
|
||||
{
|
||||
sc.socket().close();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private final void readPacket(SelectionKey key, MMOConnection<T> con)
|
||||
{
|
||||
if (con.isClosed())
|
||||
{
|
||||
return;
|
||||
}
|
||||
ByteBuffer buf = con.getReadBuffer();
|
||||
if (buf == null)
|
||||
{
|
||||
buf = READ_BUFFER;
|
||||
}
|
||||
|
||||
// if we try to to do a read with no space in the buffer it will read 0 bytes going into infinite loop
|
||||
if (buf.position() == buf.limit())
|
||||
{
|
||||
System.exit(0);
|
||||
}
|
||||
|
||||
int result = -2;
|
||||
|
||||
boolean critical = true;
|
||||
|
||||
try
|
||||
{
|
||||
result = con.read(buf);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
if (!con.isConnected() || !con.isChannelConnected())
|
||||
{
|
||||
critical = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (result > 0)
|
||||
{
|
||||
buf.flip();
|
||||
|
||||
final T client = con.getClient();
|
||||
|
||||
for (int i = 0; i < MAX_READ_PER_PASS; i++)
|
||||
{
|
||||
if (!tryReadPacket(key, client, buf, con))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// only reachable if MAX_READ_PER_PASS has been reached
|
||||
// check if there are some more bytes in buffer
|
||||
// and allocate/compact to prevent content lose.
|
||||
if (buf.remaining() > 0)
|
||||
{
|
||||
// did we use the READ_BUFFER ?
|
||||
if (buf == READ_BUFFER)
|
||||
{
|
||||
// move the pending byte to the connections READ_BUFFER
|
||||
allocateReadBuffer(con);
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the first byte to the beginning :)
|
||||
buf.compact();
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (result)
|
||||
{
|
||||
case 0:
|
||||
case -1:
|
||||
{
|
||||
closeConnectionImpl(key, con);
|
||||
break;
|
||||
}
|
||||
case -2:
|
||||
{
|
||||
con.getClient().onForcedDisconnection(critical);
|
||||
closeConnectionImpl(key, con);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean tryReadPacket(SelectionKey key, T client, ByteBuffer buf, MMOConnection<T> con)
|
||||
{
|
||||
switch (buf.remaining())
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
// buffer is full nothing to read
|
||||
return false;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
// we don`t have enough data for header so we need to read
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||
|
||||
// did we use the READ_BUFFER ?
|
||||
if (buf == READ_BUFFER)
|
||||
{
|
||||
// move the pending byte to the connections READ_BUFFER
|
||||
allocateReadBuffer(con);
|
||||
}
|
||||
else
|
||||
{
|
||||
// move the first byte to the beginning :)
|
||||
buf.compact();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// data size excluding header size :>
|
||||
final int dataPending = (buf.getShort() & 0xFFFF) - HEADER_SIZE;
|
||||
|
||||
// do we got enough bytes for the packet?
|
||||
if (dataPending <= buf.remaining())
|
||||
{
|
||||
boolean read = true;
|
||||
// avoid parsing dummy packets (packets without body)
|
||||
if (dataPending > 0)
|
||||
{
|
||||
final int pos = buf.position();
|
||||
if (!parseClientPacket(pos, buf, dataPending, client))
|
||||
{
|
||||
read = false;
|
||||
}
|
||||
buf.position(pos + dataPending);
|
||||
}
|
||||
// if we are done with this buffer
|
||||
if (!buf.hasRemaining())
|
||||
{
|
||||
if (buf != READ_BUFFER)
|
||||
{
|
||||
con.setReadBuffer(null);
|
||||
recycleBuffer(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
READ_BUFFER.clear();
|
||||
}
|
||||
read = false;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
// we don`t have enough bytes for the dataPacket so we need
|
||||
// to read
|
||||
key.interestOps(key.interestOps() | SelectionKey.OP_READ);
|
||||
// did we use the READ_BUFFER ?
|
||||
if (buf == READ_BUFFER)
|
||||
{
|
||||
// move it`s position
|
||||
buf.position(buf.position() - HEADER_SIZE);
|
||||
// move the pending byte to the connections READ_BUFFER
|
||||
allocateReadBuffer(con);
|
||||
}
|
||||
else
|
||||
{
|
||||
buf.position(buf.position() - HEADER_SIZE);
|
||||
buf.compact();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private final void allocateReadBuffer(MMOConnection<T> con)
|
||||
{
|
||||
con.setReadBuffer(getPooledBuffer().put(READ_BUFFER));
|
||||
READ_BUFFER.clear();
|
||||
}
|
||||
|
||||
private final boolean parseClientPacket(int pos, ByteBuffer buf, int dataSize, T client)
|
||||
{
|
||||
final boolean ret = client.decrypt(buf, dataSize);
|
||||
|
||||
if (ret && buf.hasRemaining())
|
||||
{
|
||||
// apply limit
|
||||
final int limit = buf.limit();
|
||||
buf.limit(pos + dataSize);
|
||||
|
||||
final ReceivablePacket<T> cp = _packetHandler.handlePacket(buf, client);
|
||||
|
||||
if (cp != null)
|
||||
{
|
||||
cp._buf = buf;
|
||||
cp._sbuf = STRING_BUFFER;
|
||||
cp._client = client;
|
||||
|
||||
if (cp.read())
|
||||
{
|
||||
_executor.execute(cp);
|
||||
}
|
||||
|
||||
cp._buf = null;
|
||||
cp._sbuf = null;
|
||||
}
|
||||
|
||||
buf.limit(limit);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private final void writeClosePacket(MMOConnection<T> con)
|
||||
{
|
||||
SendablePacket<T> sp;
|
||||
synchronized (con.getSendQueue())
|
||||
{
|
||||
if (con.getSendQueue().isEmpty())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
while ((sp = con.getSendQueue().removeFirst()) != null)
|
||||
{
|
||||
WRITE_BUFFER.clear();
|
||||
|
||||
putPacketIntoWriteBuffer(con.getClient(), sp);
|
||||
|
||||
WRITE_BUFFER.flip();
|
||||
|
||||
try
|
||||
{
|
||||
con.write(WRITE_BUFFER);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// error handling goes on the if bellow
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected final void writePacket(SelectionKey key, MMOConnection<T> con)
|
||||
{
|
||||
if (!prepareWriteBuffer(con))
|
||||
{
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
return;
|
||||
}
|
||||
|
||||
DIRECT_WRITE_BUFFER.flip();
|
||||
|
||||
final int size = DIRECT_WRITE_BUFFER.remaining();
|
||||
|
||||
int result = -1;
|
||||
|
||||
try
|
||||
{
|
||||
result = con.write(DIRECT_WRITE_BUFFER);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// error handling goes on the if bellow
|
||||
}
|
||||
|
||||
// check if no error happened
|
||||
if (result >= 0)
|
||||
{
|
||||
// check if we written everything
|
||||
if (result == size)
|
||||
{
|
||||
// complete write
|
||||
synchronized (con.getSendQueue())
|
||||
{
|
||||
if (con.getSendQueue().isEmpty() && !con.hasPendingWriteBuffer())
|
||||
{
|
||||
key.interestOps(key.interestOps() & ~SelectionKey.OP_WRITE);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// incomplete write
|
||||
con.createWriteBuffer(DIRECT_WRITE_BUFFER);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
con.getClient().onForcedDisconnection(true);
|
||||
closeConnectionImpl(key, con);
|
||||
}
|
||||
}
|
||||
|
||||
private final boolean prepareWriteBuffer(MMOConnection<T> con)
|
||||
{
|
||||
boolean hasPending = false;
|
||||
DIRECT_WRITE_BUFFER.clear();
|
||||
|
||||
// if there is pending content add it
|
||||
if (con.hasPendingWriteBuffer())
|
||||
{
|
||||
con.movePendingWriteBufferTo(DIRECT_WRITE_BUFFER);
|
||||
hasPending = true;
|
||||
}
|
||||
|
||||
if ((DIRECT_WRITE_BUFFER.remaining() > 1) && !con.hasPendingWriteBuffer())
|
||||
{
|
||||
final NioNetStackList<SendablePacket<T>> sendQueue = con.getSendQueue();
|
||||
final T client = con.getClient();
|
||||
SendablePacket<T> sp;
|
||||
|
||||
for (int i = 0; i < MAX_SEND_PER_PASS; i++)
|
||||
{
|
||||
synchronized (con.getSendQueue())
|
||||
{
|
||||
if (sendQueue.isEmpty())
|
||||
{
|
||||
sp = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
sp = sendQueue.removeFirst();
|
||||
}
|
||||
}
|
||||
|
||||
if (sp == null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
hasPending = true;
|
||||
|
||||
// put into WriteBuffer
|
||||
putPacketIntoWriteBuffer(client, sp);
|
||||
|
||||
WRITE_BUFFER.flip();
|
||||
|
||||
if (DIRECT_WRITE_BUFFER.remaining() < WRITE_BUFFER.limit())
|
||||
{
|
||||
con.createWriteBuffer(WRITE_BUFFER);
|
||||
break;
|
||||
}
|
||||
DIRECT_WRITE_BUFFER.put(WRITE_BUFFER);
|
||||
}
|
||||
}
|
||||
return hasPending;
|
||||
}
|
||||
|
||||
private final void putPacketIntoWriteBuffer(T client, SendablePacket<T> sp)
|
||||
{
|
||||
WRITE_BUFFER.clear();
|
||||
|
||||
// reserve space for the size
|
||||
final int headerPos = WRITE_BUFFER.position();
|
||||
final int dataPos = headerPos + HEADER_SIZE;
|
||||
WRITE_BUFFER.position(dataPos);
|
||||
|
||||
// set the write buffer
|
||||
sp._buf = WRITE_BUFFER;
|
||||
// write content to buffer
|
||||
sp.write();
|
||||
// delete the write buffer
|
||||
sp._buf = null;
|
||||
|
||||
// size (inclusive header)
|
||||
int dataSize = WRITE_BUFFER.position() - dataPos;
|
||||
WRITE_BUFFER.position(dataPos);
|
||||
client.encrypt(WRITE_BUFFER, dataSize);
|
||||
|
||||
// recalculate size after encryption
|
||||
dataSize = WRITE_BUFFER.position() - dataPos;
|
||||
|
||||
WRITE_BUFFER.position(headerPos);
|
||||
// write header
|
||||
WRITE_BUFFER.putShort((short) (dataSize + HEADER_SIZE));
|
||||
WRITE_BUFFER.position(dataPos + dataSize);
|
||||
}
|
||||
|
||||
final void closeConnection(MMOConnection<T> con)
|
||||
{
|
||||
synchronized (_pendingClose)
|
||||
{
|
||||
_pendingClose.addLast(con);
|
||||
}
|
||||
}
|
||||
|
||||
private final void closeConnectionImpl(SelectionKey key, MMOConnection<T> con)
|
||||
{
|
||||
try
|
||||
{
|
||||
// notify connection
|
||||
con.getClient().onDisconnection();
|
||||
}
|
||||
finally
|
||||
{
|
||||
try
|
||||
{
|
||||
// close socket and the SocketChannel
|
||||
con.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// ignore, we are closing anyway
|
||||
}
|
||||
finally
|
||||
{
|
||||
con.releaseBuffers();
|
||||
// clear attachment
|
||||
key.attach(null);
|
||||
// cancel key
|
||||
key.cancel();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public final void shutdown()
|
||||
{
|
||||
_shutdown = true;
|
||||
}
|
||||
|
||||
public boolean isShutdown()
|
||||
{
|
||||
return _shutdown;
|
||||
}
|
||||
|
||||
protected void closeSelectorThread()
|
||||
{
|
||||
for (SelectionKey key : _selector.keys())
|
||||
{
|
||||
try
|
||||
{
|
||||
key.channel().close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// ignore
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
_selector.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* 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.mmocore;
|
||||
|
||||
/**
|
||||
* @author KenM
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class SendablePacket<T extends MMOClient<?>>extends AbstractPacket<T>
|
||||
{
|
||||
protected final void putInt(int value)
|
||||
{
|
||||
_buf.putInt(value);
|
||||
}
|
||||
|
||||
protected final void putDouble(double value)
|
||||
{
|
||||
_buf.putDouble(value);
|
||||
}
|
||||
|
||||
protected final void putFloat(float value)
|
||||
{
|
||||
_buf.putFloat(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write <B>byte</B> to the buffer. <BR>
|
||||
* 8bit integer (00)
|
||||
* @param data
|
||||
*/
|
||||
protected final void writeC(int data)
|
||||
{
|
||||
_buf.put((byte) data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write <B>double</B> to the buffer. <BR>
|
||||
* 64bit double precision float (00 00 00 00 00 00 00 00)
|
||||
* @param value
|
||||
*/
|
||||
protected final void writeF(double value)
|
||||
{
|
||||
_buf.putDouble(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write <B>short</B> to the buffer. <BR>
|
||||
* 16bit integer (00 00)
|
||||
* @param value
|
||||
*/
|
||||
protected final void writeH(int value)
|
||||
{
|
||||
_buf.putShort((short) value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write <B>int</B> to the buffer. <BR>
|
||||
* 32bit integer (00 00 00 00)
|
||||
* @param value
|
||||
*/
|
||||
protected final void writeD(int value)
|
||||
{
|
||||
_buf.putInt(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write <B>long</B> to the buffer. <BR>
|
||||
* 64bit integer (00 00 00 00 00 00 00 00)
|
||||
* @param value
|
||||
*/
|
||||
protected final void writeQ(long value)
|
||||
{
|
||||
_buf.putLong(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write <B>byte[]</B> to the buffer. <BR>
|
||||
* 8bit integer array (00 ...)
|
||||
* @param data
|
||||
*/
|
||||
protected final void writeB(byte[] data)
|
||||
{
|
||||
_buf.put(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write <B>String</B> to the buffer.
|
||||
* @param text
|
||||
*/
|
||||
protected final void writeS(String text)
|
||||
{
|
||||
if (text != null)
|
||||
{
|
||||
final int len = text.length();
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
_buf.putChar(text.charAt(i));
|
||||
}
|
||||
}
|
||||
|
||||
_buf.putChar('\000');
|
||||
}
|
||||
|
||||
protected abstract void write();
|
||||
}
|
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
public class ClassMasterSettings
|
||||
{
|
||||
private final Map<Integer, Map<Integer, Integer>> _claimItems;
|
||||
private final Map<Integer, Map<Integer, Integer>> _rewardItems;
|
||||
private final Map<Integer, Boolean> _allowedClassChange;
|
||||
|
||||
public ClassMasterSettings(String _configLine)
|
||||
{
|
||||
_claimItems = new HashMap<>();
|
||||
_rewardItems = new HashMap<>();
|
||||
_allowedClassChange = new HashMap<>();
|
||||
if (_configLine != null)
|
||||
{
|
||||
parseConfigLine(_configLine.trim());
|
||||
}
|
||||
}
|
||||
|
||||
private void parseConfigLine(String _configLine)
|
||||
{
|
||||
final StringTokenizer st = new StringTokenizer(_configLine, ";");
|
||||
|
||||
while (st.hasMoreTokens())
|
||||
{
|
||||
final int job = Integer.parseInt(st.nextToken());
|
||||
|
||||
_allowedClassChange.put(job, true);
|
||||
|
||||
Map<Integer, Integer> _items = new HashMap<>();
|
||||
|
||||
if (st.hasMoreTokens())
|
||||
{
|
||||
final StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],");
|
||||
|
||||
while (st2.hasMoreTokens())
|
||||
{
|
||||
final StringTokenizer st3 = new StringTokenizer(st2.nextToken(), "()");
|
||||
final int _itemId = Integer.parseInt(st3.nextToken());
|
||||
final int _quantity = Integer.parseInt(st3.nextToken());
|
||||
_items.put(_itemId, _quantity);
|
||||
}
|
||||
}
|
||||
|
||||
_claimItems.put(job, _items);
|
||||
_items = new HashMap<>();
|
||||
|
||||
if (st.hasMoreTokens())
|
||||
{
|
||||
final StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],");
|
||||
|
||||
while (st2.hasMoreTokens())
|
||||
{
|
||||
final StringTokenizer st3 = new StringTokenizer(st2.nextToken(), "()");
|
||||
final int _itemId = Integer.parseInt(st3.nextToken());
|
||||
final int _quantity = Integer.parseInt(st3.nextToken());
|
||||
_items.put(_itemId, _quantity);
|
||||
}
|
||||
}
|
||||
_rewardItems.put(job, _items);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isAllowed(int job)
|
||||
{
|
||||
if (_allowedClassChange == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (_allowedClassChange.containsKey(job))
|
||||
{
|
||||
return _allowedClassChange.get(job);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> getRewardItems(int job)
|
||||
{
|
||||
if (_rewardItems.containsKey(job))
|
||||
{
|
||||
return _rewardItems.get(job);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public Map<Integer, Integer> getRequireItems(int job)
|
||||
{
|
||||
if (_claimItems.containsKey(job))
|
||||
{
|
||||
return _claimItems.get(job);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
@ -0,0 +1,159 @@
|
||||
/*
|
||||
* 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
|
||||
{
|
||||
final byte[] _addr;
|
||||
final byte[] _mask;
|
||||
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 IPSubnet(InetAddress addr, int mask) throws UnknownHostException
|
||||
{
|
||||
_addr = addr.getAddress();
|
||||
_isIPv4 = _addr.length == 4;
|
||||
_mask = getMask(mask, _addr.length);
|
||||
if (!applyMask(_addr))
|
||||
{
|
||||
throw new UnknownHostException(addr + "/" + mask);
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] getAddress()
|
||||
{
|
||||
return _addr;
|
||||
}
|
||||
|
||||
public 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
// check for embedded v4 in v6 addr (not done !)
|
||||
else 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());
|
||||
}
|
||||
return (o instanceof InetAddress) && applyMask(((InetAddress) o).getAddress());
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
@ -0,0 +1,143 @@
|
||||
/*
|
||||
* 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.nio.channels.SocketChannel;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map.Entry;
|
||||
|
||||
import com.l2jmobius.commons.mmocore.IAcceptFilter;
|
||||
|
||||
/**
|
||||
* Formatted Forsaiken's IPv4 filter [DrHouse]
|
||||
* @author Forsaiken
|
||||
*/
|
||||
public class IPv4Filter implements IAcceptFilter, Runnable
|
||||
{
|
||||
private final HashMap<Integer, Flood> _ipFloodMap;
|
||||
private static final long SLEEP_TIME = 5000;
|
||||
|
||||
public IPv4Filter()
|
||||
{
|
||||
_ipFloodMap = new HashMap<>();
|
||||
final Thread t = new Thread(this);
|
||||
t.setName(getClass().getSimpleName());
|
||||
t.setDaemon(true);
|
||||
t.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ip
|
||||
* @return
|
||||
*/
|
||||
private static final int hash(byte[] ip)
|
||||
{
|
||||
return (ip[0] & 0xFF) | ((ip[1] << 8) & 0xFF00) | ((ip[2] << 16) & 0xFF0000) | ((ip[3] << 24) & 0xFF000000);
|
||||
}
|
||||
|
||||
protected static final class Flood
|
||||
{
|
||||
long lastAccess;
|
||||
int trys;
|
||||
|
||||
Flood()
|
||||
{
|
||||
lastAccess = System.currentTimeMillis();
|
||||
trys = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean accept(SocketChannel sc)
|
||||
{
|
||||
final InetAddress addr = sc.socket().getInetAddress();
|
||||
final int h = hash(addr.getAddress());
|
||||
|
||||
final long current = System.currentTimeMillis();
|
||||
Flood f;
|
||||
synchronized (_ipFloodMap)
|
||||
{
|
||||
f = _ipFloodMap.get(h);
|
||||
}
|
||||
if (f != null)
|
||||
{
|
||||
if (f.trys == -1)
|
||||
{
|
||||
f.lastAccess = current;
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((f.lastAccess + 1000) > current)
|
||||
{
|
||||
f.lastAccess = current;
|
||||
|
||||
if (f.trys >= 3)
|
||||
{
|
||||
f.trys = -1;
|
||||
return false;
|
||||
}
|
||||
|
||||
f.trys++;
|
||||
}
|
||||
else
|
||||
{
|
||||
f.lastAccess = current;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
synchronized (_ipFloodMap)
|
||||
{
|
||||
_ipFloodMap.put(h, new Flood());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run()
|
||||
{
|
||||
while (true)
|
||||
{
|
||||
final long reference = System.currentTimeMillis() - (1000 * 300);
|
||||
synchronized (_ipFloodMap)
|
||||
{
|
||||
final Iterator<Entry<Integer, Flood>> it = _ipFloodMap.entrySet().iterator();
|
||||
while (it.hasNext())
|
||||
{
|
||||
final Flood f = it.next().getValue();
|
||||
if (f.lastAccess < reference)
|
||||
{
|
||||
it.remove();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
Thread.sleep(SLEEP_TIME);
|
||||
}
|
||||
catch (InterruptedException e)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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 java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.Reader;
|
||||
import java.util.Properties;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
|
||||
public final class L2Properties extends Properties
|
||||
{
|
||||
protected static final Logger LOGGER = Logger.getLogger(Config.class.getName());
|
||||
|
||||
private boolean _warn = false;
|
||||
|
||||
public L2Properties()
|
||||
{
|
||||
}
|
||||
|
||||
public L2Properties setLog(boolean warn)
|
||||
{
|
||||
_warn = warn;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public L2Properties(String name) throws IOException
|
||||
{
|
||||
load(new FileInputStream(name));
|
||||
}
|
||||
|
||||
public L2Properties(File file) throws IOException
|
||||
{
|
||||
load(new FileInputStream(file));
|
||||
}
|
||||
|
||||
public L2Properties(InputStream inStream)
|
||||
{
|
||||
load(inStream);
|
||||
}
|
||||
|
||||
public L2Properties(Reader reader)
|
||||
{
|
||||
load(reader);
|
||||
}
|
||||
|
||||
public void load(String name) throws IOException
|
||||
{
|
||||
load(new FileInputStream(name));
|
||||
}
|
||||
|
||||
public void load(File file) throws IOException
|
||||
{
|
||||
load(new FileInputStream(file));
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void load(InputStream inStream)
|
||||
{
|
||||
try
|
||||
{
|
||||
super.load(inStream);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (inStream != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
inStream.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void load(Reader reader)
|
||||
{
|
||||
try
|
||||
{
|
||||
super.load(reader);
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (reader != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
reader.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key)
|
||||
{
|
||||
final String property = super.getProperty(key);
|
||||
|
||||
if (property == null)
|
||||
{
|
||||
if (_warn)
|
||||
{
|
||||
LOGGER.warning("L2Properties: Missing property for key - " + key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return property.trim();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getProperty(String key, String defaultValue)
|
||||
{
|
||||
final String property = super.getProperty(key, defaultValue);
|
||||
|
||||
if (property == null)
|
||||
{
|
||||
if (_warn)
|
||||
{
|
||||
LOGGER.warning("L2Properties: Missing defaultValue for key - " + key);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
return property.trim();
|
||||
}
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* @author programmos
|
||||
*/
|
||||
public class Memory
|
||||
{
|
||||
public static long getUsedMemory()
|
||||
{
|
||||
return (Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / 1048576;
|
||||
}
|
||||
|
||||
public static long getFreeMemory()
|
||||
{
|
||||
return ((Runtime.getRuntime().maxMemory() - Runtime.getRuntime().totalMemory()) + Runtime.getRuntime().freeMemory()) / 1048576;
|
||||
}
|
||||
|
||||
public static long getTotalMemory()
|
||||
{
|
||||
return Runtime.getRuntime().maxMemory() / 1048576;
|
||||
}
|
||||
}
|
@ -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.util;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
|
||||
public class OlympiadLogger
|
||||
{
|
||||
private static final Logger LOGGER = Logger.getLogger(OlympiadLogger.class.getName());
|
||||
|
||||
public static final void add(String text, String cat)
|
||||
{
|
||||
String date = new SimpleDateFormat("yy.MM.dd H:mm:ss").format(new Date());
|
||||
|
||||
new File("log/game").mkdirs();
|
||||
final File file = new File("log/game/" + (cat != null ? cat : "_all") + ".txt");
|
||||
FileWriter save = null;
|
||||
try
|
||||
{
|
||||
save = new FileWriter(file, true);
|
||||
final String out = "[" + date + "] '---': " + text + "\n"; // "+char_name()+"
|
||||
save.write(out);
|
||||
save.flush();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warning("saving chat LOGGER failed: " + e);
|
||||
e.printStackTrace();
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
||||
if (save != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
save.close();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (cat != null)
|
||||
{
|
||||
add(text, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static final void Assert(boolean exp, String cmt)
|
||||
{
|
||||
if (exp || !Config.ASSERT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LOGGER.info("Assertion error [" + cmt + "]");
|
||||
Thread.dumpStack();
|
||||
}
|
||||
}
|
@ -0,0 +1,165 @@
|
||||
/*
|
||||
* 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.Serializable;
|
||||
|
||||
/**
|
||||
* This class ...
|
||||
* @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
|
||||
*/
|
||||
public class Point3D implements Serializable
|
||||
{
|
||||
private volatile int _x, _y, _z;
|
||||
|
||||
public Point3D(int pX, int pY, int pZ)
|
||||
{
|
||||
_x = pX;
|
||||
_y = pY;
|
||||
_z = pZ;
|
||||
}
|
||||
|
||||
public Point3D(int pX, int pY)
|
||||
{
|
||||
_x = pX;
|
||||
_y = pY;
|
||||
_z = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param worldPosition
|
||||
*/
|
||||
public Point3D(Point3D worldPosition)
|
||||
{
|
||||
synchronized (worldPosition)
|
||||
{
|
||||
_x = worldPosition._x;
|
||||
_y = worldPosition._y;
|
||||
_z = worldPosition._z;
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void setTo(Point3D point)
|
||||
{
|
||||
synchronized (point)
|
||||
{
|
||||
_x = point._x;
|
||||
_y = point._y;
|
||||
_z = point._z;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return "(" + _x + ", " + _y + ", " + _z + ")";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return _x ^ _y ^ _z;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean equals(Object o)
|
||||
{
|
||||
if (o instanceof Point3D)
|
||||
{
|
||||
final Point3D point3D = (Point3D) o;
|
||||
boolean ret;
|
||||
synchronized (point3D)
|
||||
{
|
||||
ret = (point3D._x == _x) && (point3D._y == _y) && (point3D._z == _z);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public synchronized boolean equals(int pX, int pY, int pZ)
|
||||
{
|
||||
return (_x == pX) && (_y == pY) && (_z == pZ);
|
||||
}
|
||||
|
||||
public synchronized long distanceSquaredTo(Point3D point)
|
||||
{
|
||||
long dx, dy;
|
||||
synchronized (point)
|
||||
{
|
||||
dx = _x - point._x;
|
||||
dy = _y - point._y;
|
||||
}
|
||||
return (dx * dx) + (dy * dy);
|
||||
}
|
||||
|
||||
public static long distanceSquared(Point3D point1, Point3D point2)
|
||||
{
|
||||
long dx, dy;
|
||||
synchronized (point1)
|
||||
{
|
||||
synchronized (point2)
|
||||
{
|
||||
dx = point1._x - point2._x;
|
||||
dy = point1._y - point2._y;
|
||||
}
|
||||
}
|
||||
return (dx * dx) + (dy * dy);
|
||||
}
|
||||
|
||||
public static boolean distanceLessThan(Point3D point1, Point3D point2, double distance)
|
||||
{
|
||||
return distanceSquared(point1, point2) < (distance * distance);
|
||||
}
|
||||
|
||||
public synchronized int getX()
|
||||
{
|
||||
return _x;
|
||||
}
|
||||
|
||||
public synchronized void setX(int pX)
|
||||
{
|
||||
_x = pX;
|
||||
}
|
||||
|
||||
public synchronized int getY()
|
||||
{
|
||||
return _y;
|
||||
}
|
||||
|
||||
public synchronized void setY(int pY)
|
||||
{
|
||||
_y = pY;
|
||||
}
|
||||
|
||||
public synchronized int getZ()
|
||||
{
|
||||
return _z;
|
||||
}
|
||||
|
||||
public synchronized void setZ(int pZ)
|
||||
{
|
||||
_z = pZ;
|
||||
}
|
||||
|
||||
public synchronized void setXYZ(int pX, int pY, int pZ)
|
||||
{
|
||||
_x = pX;
|
||||
_y = pY;
|
||||
_z = pZ;
|
||||
}
|
||||
}
|
@ -0,0 +1,346 @@
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* Modified for Trove to use the java.util.Arrays sort/search
|
||||
* algorithms instead of those provided with colt.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Used to keep hash table capacities prime numbers. Not of interest for users; only for implementors of hashtables.
|
||||
* <p>
|
||||
* Choosing prime numbers as hash table capacities is a good idea to keep them working fast, particularly under hash table expansions.
|
||||
* <p>
|
||||
* However, JDK 1.2, JGL 3.1 and many other toolkits do nothing to keep capacities prime. This class provides efficient means to choose prime capacities.
|
||||
* <p>
|
||||
* Choosing a prime is <tt>O(LOGGER 300)</tt> (binary search in a list of 300 ints). Memory requirements: 1 KB static memory.
|
||||
* @author wolfgang.hoschek@cern.ch
|
||||
* @version 1.0, 09/24/99
|
||||
*/
|
||||
public final class PrimeFinder
|
||||
{
|
||||
/**
|
||||
* The largest prime this class can generate; currently equal to <tt>Integer.MAX_VALUE</tt>.
|
||||
*/
|
||||
public static final int LARGEST_PRIME = Integer.MAX_VALUE; // yes, it is prime.
|
||||
|
||||
/**
|
||||
* The prime number list consists of 11 chunks. Each chunk contains prime numbers. A chunk starts with a prime P1. The next element is a prime P2. P2 is the smallest prime for which holds: P2 >= 2*P1. The next element is P3, for which the same holds with respect to P2, and so on. Chunks are
|
||||
* chosen such that for any desired capacity >= 1000 the list includes a prime number <= desired capacity * 1.11. Therefore, primes can be retrieved which are quite close to any desired capacity, which in turn avoids wasting memory. For example, the list includes
|
||||
* 1039,1117,1201,1277,1361,1439,1523,1597,1759,1907,2081. So if you need a prime >= 1040, you will find a prime <= 1040*1.11=1154. Chunks are chosen such that they are optimized for a hashtable growthfactor of 2.0; If your hashtable has such a growthfactor then, after initially "rounding to a
|
||||
* prime" upon hashtable construction, it will later expand to prime capacities such that there exist no better primes. In total these are about 32*10=320 numbers -> 1 KB of static memory needed. If you are stingy, then delete every second or fourth chunk.
|
||||
*/
|
||||
|
||||
private static final int[] PRIME_CAPACITIES =
|
||||
{
|
||||
// chunk #0
|
||||
LARGEST_PRIME,
|
||||
|
||||
// chunk #1
|
||||
5,
|
||||
11,
|
||||
23,
|
||||
47,
|
||||
97,
|
||||
197,
|
||||
397,
|
||||
797,
|
||||
1597,
|
||||
3203,
|
||||
6421,
|
||||
12853,
|
||||
25717,
|
||||
51437,
|
||||
102877,
|
||||
205759,
|
||||
411527,
|
||||
823117,
|
||||
1646237,
|
||||
3292489,
|
||||
6584983,
|
||||
13169977,
|
||||
26339969,
|
||||
52679969,
|
||||
105359939,
|
||||
210719881,
|
||||
421439783,
|
||||
842879579,
|
||||
1685759167,
|
||||
|
||||
// chunk #2
|
||||
433,
|
||||
877,
|
||||
1759,
|
||||
3527,
|
||||
7057,
|
||||
14143,
|
||||
28289,
|
||||
56591,
|
||||
113189,
|
||||
226379,
|
||||
452759,
|
||||
905551,
|
||||
1811107,
|
||||
3622219,
|
||||
7244441,
|
||||
14488931,
|
||||
28977863,
|
||||
57955739,
|
||||
115911563,
|
||||
231823147,
|
||||
463646329,
|
||||
927292699,
|
||||
1854585413,
|
||||
|
||||
// chunk #3
|
||||
953,
|
||||
1907,
|
||||
3821,
|
||||
7643,
|
||||
15287,
|
||||
30577,
|
||||
61169,
|
||||
122347,
|
||||
244703,
|
||||
489407,
|
||||
978821,
|
||||
1957651,
|
||||
3915341,
|
||||
7830701,
|
||||
15661423,
|
||||
31322867,
|
||||
62645741,
|
||||
125291483,
|
||||
250582987,
|
||||
501165979,
|
||||
1002331963,
|
||||
2004663929,
|
||||
|
||||
// chunk #4
|
||||
1039,
|
||||
2081,
|
||||
4177,
|
||||
8363,
|
||||
16729,
|
||||
33461,
|
||||
66923,
|
||||
133853,
|
||||
267713,
|
||||
535481,
|
||||
1070981,
|
||||
2141977,
|
||||
4283963,
|
||||
8567929,
|
||||
17135863,
|
||||
34271747,
|
||||
68543509,
|
||||
137087021,
|
||||
274174111,
|
||||
548348231,
|
||||
1096696463,
|
||||
|
||||
// chunk #5
|
||||
31,
|
||||
67,
|
||||
137,
|
||||
277,
|
||||
557,
|
||||
1117,
|
||||
2237,
|
||||
4481,
|
||||
8963,
|
||||
17929,
|
||||
35863,
|
||||
71741,
|
||||
143483,
|
||||
286973,
|
||||
573953,
|
||||
1147921,
|
||||
2295859,
|
||||
4591721,
|
||||
9183457,
|
||||
18366923,
|
||||
36733847,
|
||||
73467739,
|
||||
146935499,
|
||||
293871013,
|
||||
587742049,
|
||||
1175484103,
|
||||
|
||||
// chunk #6
|
||||
599,
|
||||
1201,
|
||||
2411,
|
||||
4831,
|
||||
9677,
|
||||
19373,
|
||||
38747,
|
||||
77509,
|
||||
155027,
|
||||
310081,
|
||||
620171,
|
||||
1240361,
|
||||
2480729,
|
||||
4961459,
|
||||
9922933,
|
||||
19845871,
|
||||
39691759,
|
||||
79383533,
|
||||
158767069,
|
||||
317534141,
|
||||
635068283,
|
||||
1270136683,
|
||||
|
||||
// chunk #7
|
||||
311,
|
||||
631,
|
||||
1277,
|
||||
2557,
|
||||
5119,
|
||||
10243,
|
||||
20507,
|
||||
41017,
|
||||
82037,
|
||||
164089,
|
||||
328213,
|
||||
656429,
|
||||
1312867,
|
||||
2625761,
|
||||
5251529,
|
||||
10503061,
|
||||
21006137,
|
||||
42012281,
|
||||
84024581,
|
||||
168049163,
|
||||
336098327,
|
||||
672196673,
|
||||
1344393353,
|
||||
|
||||
// chunk #8
|
||||
3,
|
||||
7,
|
||||
17,
|
||||
37,
|
||||
79,
|
||||
163,
|
||||
331,
|
||||
673,
|
||||
1361,
|
||||
2729,
|
||||
5471,
|
||||
10949,
|
||||
21911,
|
||||
43853,
|
||||
87719,
|
||||
175447,
|
||||
350899,
|
||||
701819,
|
||||
1403641,
|
||||
2807303,
|
||||
5614657,
|
||||
11229331,
|
||||
22458671,
|
||||
44917381,
|
||||
89834777,
|
||||
179669557,
|
||||
359339171,
|
||||
718678369,
|
||||
1437356741,
|
||||
|
||||
// chunk #9
|
||||
43,
|
||||
89,
|
||||
179,
|
||||
359,
|
||||
719,
|
||||
1439,
|
||||
2879,
|
||||
5779,
|
||||
11579,
|
||||
23159,
|
||||
46327,
|
||||
92657,
|
||||
185323,
|
||||
370661,
|
||||
741337,
|
||||
1482707,
|
||||
2965421,
|
||||
5930887,
|
||||
11861791,
|
||||
23723597,
|
||||
47447201,
|
||||
94894427,
|
||||
189788857,
|
||||
379577741,
|
||||
759155483,
|
||||
1518310967,
|
||||
|
||||
// chunk #10
|
||||
379,
|
||||
761,
|
||||
1523,
|
||||
3049,
|
||||
6101,
|
||||
12203,
|
||||
24407,
|
||||
48817,
|
||||
97649,
|
||||
195311,
|
||||
390647,
|
||||
781301,
|
||||
1562611,
|
||||
3125257,
|
||||
6250537,
|
||||
12501169,
|
||||
25002389,
|
||||
50004791,
|
||||
100009607,
|
||||
200019221,
|
||||
400038451,
|
||||
800076929,
|
||||
1600153859
|
||||
};
|
||||
|
||||
static
|
||||
{
|
||||
// initializer
|
||||
// The above prime numbers are formatted for human readability.
|
||||
// To find numbers fast, we sort them once and for all.
|
||||
|
||||
Arrays.sort(PRIME_CAPACITIES);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a prime number which is <code>>= desiredCapacity</code> and very close to <code>desiredCapacity</code> (within 11% if <code>desiredCapacity >= 1000</code>).
|
||||
* @param desiredCapacity the capacity desired by the user.
|
||||
* @return the capacity which should be used for a hashtable.
|
||||
*/
|
||||
public static final int nextPrime(int desiredCapacity)
|
||||
{
|
||||
int i = Arrays.binarySearch(PRIME_CAPACITIES, desiredCapacity);
|
||||
if (i < 0)
|
||||
{
|
||||
// desired capacity not found, choose next prime greater
|
||||
// than desired capacity
|
||||
i = -i - 1; // remember the semantics of binarySearch...
|
||||
}
|
||||
return PRIME_CAPACITIES[i];
|
||||
}
|
||||
}
|
479
L2J_Mobius_C6_Interlude/java/com/l2jmobius/commons/util/Rnd.java
Normal file
479
L2J_Mobius_C6_Interlude/java/com/l2jmobius/commons/util/Rnd.java
Normal file
@ -0,0 +1,479 @@
|
||||
/*
|
||||
* 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.List;
|
||||
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
|
||||
*/
|
||||
public 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
|
||||
*/
|
||||
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,
|
||||
|
||||
/**
|
||||
* Each thread has it`s own random instance.<br>
|
||||
* Provides best parallel access speed.
|
||||
*/
|
||||
UNSECURE_THREAD_LOCAL,
|
||||
|
||||
/**
|
||||
* Provides much faster parallel access speed.
|
||||
*/
|
||||
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<Seed>()
|
||||
{
|
||||
@Override
|
||||
public final Seed initialValue()
|
||||
{
|
||||
return new Seed(++SEED_UNIQUIFIER + System.nanoTime());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public ThreadLocalRandom(long seed)
|
||||
{
|
||||
_seedLocal = new ThreadLocal<Seed>()
|
||||
{
|
||||
@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 final Random directRandom()
|
||||
{
|
||||
return rnd.directRandom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a random double number from 0 to 1
|
||||
* @return A random double number from 0 to 1
|
||||
*/
|
||||
public static final 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 final 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 final 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 final long get(long min, long max)
|
||||
{
|
||||
return rnd.get(min, max);
|
||||
}
|
||||
|
||||
public static final 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 final 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 final 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 final 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 final 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 final 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 final int nextInt()
|
||||
{
|
||||
return rnd.nextInt();
|
||||
}
|
||||
|
||||
public static final 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 final long nextLong()
|
||||
{
|
||||
return rnd.nextLong();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a randomly selected element taken from the given list.
|
||||
* @param <T> type of list elements.
|
||||
* @param list a list.
|
||||
* @return a randomly selected element.
|
||||
*/
|
||||
public static final <T> T get(List<T> list)
|
||||
{
|
||||
if ((list == null) || (list.size() == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return list.get(get(list.size()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a randomly selected element taken from the given array.
|
||||
* @param <T> type of array elements.
|
||||
* @param array an array.
|
||||
* @return a randomly selected element.
|
||||
*/
|
||||
public static final <T> T get(T[] array)
|
||||
{
|
||||
if ((array == null) || (array.length == 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
return array[get(array.length)];
|
||||
}
|
||||
}
|
@ -0,0 +1,312 @@
|
||||
/*
|
||||
* 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.text.NumberFormat;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
|
||||
/**
|
||||
* String utilities optimized for the best performance.
|
||||
* <h1>How to Use It</h1>
|
||||
* <h2>concat() or append()</h2> If concatenating strings in single call, use StringUtil.concat(), otherwise use StringUtil.append() and its variants.
|
||||
* <h2>Minimum Calls</h2> Bad:
|
||||
*
|
||||
* <pre>
|
||||
* final StringBuilder sbString = new StringBuilder();
|
||||
* StringUtil.append(sbString, "text 1", String.valueOf(npcId));
|
||||
* StringUtil.append("text 2");
|
||||
* </pre>
|
||||
*
|
||||
* Good:
|
||||
*
|
||||
* <pre>
|
||||
* final StringBuilder sbString = new StringBuilder();
|
||||
* StringUtil.append(sbString, "text 1", String.valueOf(npcId), "text 2");
|
||||
* </pre>
|
||||
*
|
||||
* Why?<br/>
|
||||
* Because the less calls you do, the less memory re-allocations have to be done so the whole text fits into the memory and less array copy tasks has to be performed. So if using less calls, less memory is used and string concatenation is faster.
|
||||
* <h2>Size Hints for Loops</h2> Bad:
|
||||
*
|
||||
* <pre>
|
||||
* final StringBuilder sbString = new StringBuilder();
|
||||
* StringUtil.append(sbString, "header start", someText, "header end");
|
||||
* for (int i = 0; i < 50; i++)
|
||||
* {
|
||||
* StringUtil.append(sbString, "text 1", stringArray[i], "text 2");
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* Good:
|
||||
*
|
||||
* <pre>
|
||||
* final StringBuilder sbString = StringUtil.startAppend(1300, "header start", someText, "header end");
|
||||
* for (int i = 0; i < 50; i++)
|
||||
* {
|
||||
* StringUtil.append(sbString, "text 1", stringArray[i], "text 2");
|
||||
* }
|
||||
* </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>.
|
||||
* <h2>Concatenation and Constants</h2> Bad:
|
||||
*
|
||||
* <pre>
|
||||
* StringUtil.concat("text 1 ", "text 2", String.valueOf(npcId));
|
||||
* </pre>
|
||||
*
|
||||
* Good:
|
||||
*
|
||||
* <pre>
|
||||
* StringUtil.concat("text 1 " + "text 2", String.valueOf(npcId));
|
||||
* </pre>
|
||||
*
|
||||
* or
|
||||
*
|
||||
* <pre>
|
||||
* StringUtil.concat("text 1 text 2", 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.
|
||||
* <h2>Concatenation and Constant Variables</h2> Bad:
|
||||
*
|
||||
* <pre>
|
||||
* String glue = "some glue";
|
||||
* StringUtil.concat("text 1", glue, "text 2", glue, String.valueOf(npcId));
|
||||
* </pre>
|
||||
*
|
||||
* Good:
|
||||
*
|
||||
* <pre>
|
||||
* final String glue = "some glue";
|
||||
* StringUtil.concat("text 1" + glue + "text2" + 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>.
|
||||
* <h2>StringBuilder Reuse</h2> Bad:
|
||||
*
|
||||
* <pre>
|
||||
* final StringBuilder sbString1 = new StringBuilder();
|
||||
* StringUtil.append(sbString1, "text 1", String.valueOf(npcId), "text 2");
|
||||
* ... // output of sbString1, it is no more needed
|
||||
* final StringBuilder sbString2 = new StringBuilder();
|
||||
* StringUtil.append(sbString2, "text 3", String.valueOf(npcId), "text 4");
|
||||
* </pre>
|
||||
*
|
||||
* Good:
|
||||
*
|
||||
* <pre>
|
||||
* final StringBuilder sbString = new StringBuilder();
|
||||
* StringUtil.append(sbString, "text 1", String.valueOf(npcId), "text 2");
|
||||
* ... // output of sbString, it is no more needed
|
||||
* sbString.setLength(0);
|
||||
* StringUtil.append(sbString, "text 3", String.valueOf(npcId), "text 4");
|
||||
* </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().
|
||||
* <h2>How much faster is it?</h2> 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 StringBuilder. Also, with more strings concatenated, the difference between StringBuilder and StringBuilder gets larger. In code, there are many
|
||||
* cases, where there are concatenated 50+ strings so the time saving is even greater.
|
||||
*
|
||||
* <pre>
|
||||
* Count: 2
|
||||
* StringBuilder: 1893
|
||||
* StringBuilder with size: 1703
|
||||
* String: 1033
|
||||
* StringBuilder: 993
|
||||
* StringBuilder with size: 1024
|
||||
* Count: 3
|
||||
* StringBuilder: 1973
|
||||
* StringBuilder with size: 1872
|
||||
* String: 2583
|
||||
* StringBuilder: 1633
|
||||
* StringBuilder with size: 1156
|
||||
* Count: 4
|
||||
* StringBuilder: 2188
|
||||
* StringBuilder with size: 2229
|
||||
* String: 4207
|
||||
* StringBuilder: 1816
|
||||
* StringBuilder with size: 1444
|
||||
* Count: 5
|
||||
* StringBuilder: 9185
|
||||
* StringBuilder with size: 9464
|
||||
* String: 6937
|
||||
* StringBuilder: 2745
|
||||
* StringBuilder with size: 1882
|
||||
* Count: 6
|
||||
* StringBuilder: 9785
|
||||
* StringBuilder with size: 10082
|
||||
* String: 9471
|
||||
* StringBuilder: 2889
|
||||
* StringBuilder with size: 1857
|
||||
* Count: 7
|
||||
* StringBuilder: 10169
|
||||
* StringBuilder 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
|
||||
* @see StringUtil
|
||||
*/
|
||||
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
|
||||
* @see StringUtil
|
||||
*/
|
||||
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
|
||||
* @see StringUtil
|
||||
*/
|
||||
public static void append(StringBuilder sbString, String... strings)
|
||||
{
|
||||
sbString.ensureCapacity(sbString.length() + getLength(strings));
|
||||
|
||||
for (String string : strings)
|
||||
{
|
||||
sbString.append(string);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends objects to an existing StringBuilder.
|
||||
* @param sb : the StringBuilder to edit.
|
||||
* @param content : parameters to append.
|
||||
*/
|
||||
public static void append(StringBuilder sb, Object... content)
|
||||
{
|
||||
for (Object obj : content)
|
||||
{
|
||||
sb.append((obj == null) ? null : obj.toString());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts total length of all the strings.
|
||||
* @param strings array of strings
|
||||
* @return total length of all the strings
|
||||
*/
|
||||
private static int getLength(String[] strings)
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
for (String string : strings)
|
||||
{
|
||||
if (string == null)
|
||||
{
|
||||
length += 4;
|
||||
}
|
||||
else
|
||||
{
|
||||
length += 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();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param value : the number to format.
|
||||
* @return a number formatted with "," delimiter.
|
||||
*/
|
||||
public static String formatNumber(long value)
|
||||
{
|
||||
return NumberFormat.getInstance(Locale.ENGLISH).format(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string : the initial word to scramble.
|
||||
* @return an anagram of the given string.
|
||||
*/
|
||||
public static String scrambleString(String string)
|
||||
{
|
||||
final List<String> letters = Arrays.asList(string.split(""));
|
||||
Collections.shuffle(letters);
|
||||
|
||||
final StringBuilder sb = new StringBuilder(string.length());
|
||||
for (String c : letters)
|
||||
{
|
||||
sb.append(c);
|
||||
}
|
||||
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* 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.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This class ...
|
||||
* @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
|
||||
* @author luisantonioa
|
||||
*/
|
||||
public class Util
|
||||
{
|
||||
protected static final Logger LOGGER = Logger.getLogger(Util.class.getName());
|
||||
|
||||
public static boolean isInternalIP(String ipAddress)
|
||||
{
|
||||
return ipAddress.startsWith("192.168.") || ipAddress.startsWith("10.") || ipAddress.startsWith("127.0.0.1");
|
||||
}
|
||||
|
||||
public static String printData(byte[] data, int len)
|
||||
{
|
||||
final StringBuilder result = new StringBuilder();
|
||||
|
||||
int counter = 0;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if ((counter % 16) == 0)
|
||||
{
|
||||
result.append(fillHex(i, 4) + ": ");
|
||||
}
|
||||
|
||||
result.append(fillHex(data[i] & 0xff, 2) + " ");
|
||||
counter++;
|
||||
if (counter == 16)
|
||||
{
|
||||
result.append(" ");
|
||||
|
||||
int charpoint = i - 15;
|
||||
for (int a = 0; a < 16; a++)
|
||||
{
|
||||
final int t1 = data[charpoint++];
|
||||
if ((t1 > 0x1f) && (t1 < 0x80))
|
||||
{
|
||||
result.append((char) t1);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append('.');
|
||||
}
|
||||
}
|
||||
|
||||
result.append('\n');
|
||||
counter = 0;
|
||||
}
|
||||
}
|
||||
|
||||
final int rest = data.length % 16;
|
||||
if (rest > 0)
|
||||
{
|
||||
for (int i = 0; i < (17 - rest); i++)
|
||||
{
|
||||
result.append(" ");
|
||||
}
|
||||
|
||||
int charpoint = data.length - rest;
|
||||
for (int a = 0; a < rest; a++)
|
||||
{
|
||||
final int t1 = data[charpoint++];
|
||||
if ((t1 > 0x1f) && (t1 < 0x80))
|
||||
{
|
||||
result.append((char) t1);
|
||||
}
|
||||
else
|
||||
{
|
||||
result.append('.');
|
||||
}
|
||||
}
|
||||
|
||||
result.append('\n');
|
||||
}
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
public static String fillHex(int data, int digits)
|
||||
{
|
||||
String number = Integer.toHexString(data);
|
||||
|
||||
for (int i = number.length(); i < digits; i++)
|
||||
{
|
||||
number = "0" + number;
|
||||
}
|
||||
|
||||
return number;
|
||||
}
|
||||
|
||||
public static void printSection(String s)
|
||||
{
|
||||
s = "=[ " + s + " ]";
|
||||
while (s.length() < 62)
|
||||
{
|
||||
s = "-" + s;
|
||||
}
|
||||
LOGGER.info(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param raw
|
||||
* @return
|
||||
*/
|
||||
public static String printData(byte[] raw)
|
||||
{
|
||||
return printData(raw, raw.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* converts a given time from minutes -> miliseconds
|
||||
* @param minutesToConvert
|
||||
* @return
|
||||
*/
|
||||
public static int convertMinutesToMiliseconds(int minutesToConvert)
|
||||
{
|
||||
return minutesToConvert * 60000;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* @author programmos
|
||||
*/
|
||||
public abstract class XmlEngine
|
||||
{
|
||||
protected static final Logger LOGGER = Logger.getLogger(XmlEngine.class.getName());
|
||||
|
||||
private final File _file;
|
||||
|
||||
XmlEngine(File f)
|
||||
{
|
||||
_file = f;
|
||||
parseFile();
|
||||
}
|
||||
|
||||
public void parseFile()
|
||||
{
|
||||
Document document = null;
|
||||
|
||||
try
|
||||
{
|
||||
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
|
||||
factory.setValidating(false);
|
||||
factory.setIgnoringComments(true);
|
||||
document = factory.newDocumentBuilder().parse(_file);
|
||||
}
|
||||
catch (ParserConfigurationException e)
|
||||
{
|
||||
LOGGER.warning("Error loading configure XML: " + _file.getName() + " " + e);
|
||||
}
|
||||
catch (SAXException e)
|
||||
{
|
||||
e.printStackTrace();
|
||||
}
|
||||
catch (IOException e)
|
||||
{
|
||||
LOGGER.warning("Error loading file: " + _file.getName() + " " + e);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
parseDocument(document);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
LOGGER.warning("Error in file: " + _file.getName() + " " + e);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract void parseDocument(Document document) throws Exception;
|
||||
|
||||
public List<Node> parseHeadStandart(Document doc)
|
||||
{
|
||||
final List<Node> temp = new ArrayList<>();
|
||||
for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
|
||||
{
|
||||
if ("list".equalsIgnoreCase(n.getNodeName()))
|
||||
{
|
||||
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
|
||||
{
|
||||
if ("record".equalsIgnoreCase(d.getNodeName()))
|
||||
{
|
||||
for (Node e = d.getFirstChild(); e != null; e = n.getNextSibling())
|
||||
{
|
||||
if ("value".equalsIgnoreCase(n.getNodeName()))
|
||||
{
|
||||
temp.add(d);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return temp;
|
||||
}
|
||||
}
|
@ -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.util.file.filter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
/**
|
||||
* Specialized {@link FileFilter} class.<br>
|
||||
* Accepts <b>files</b> ending with ".sql" only.
|
||||
* @author Zoey76
|
||||
*/
|
||||
public class SQLFilter implements FileFilter
|
||||
{
|
||||
@Override
|
||||
public boolean accept(File f)
|
||||
{
|
||||
return (f != null) && f.isFile() && f.getName().toLowerCase().endsWith(".sql");
|
||||
}
|
||||
}
|
@ -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.util.file.filter;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
|
||||
/**
|
||||
* Specialized {@link FileFilter} class.<br>
|
||||
* Accepts files ending with ".xml" only.
|
||||
* @author mrTJO
|
||||
*/
|
||||
public class XMLFilter implements FileFilter
|
||||
{
|
||||
@Override
|
||||
public boolean accept(File f)
|
||||
{
|
||||
return (f != null) && f.isFile() && f.getName().toLowerCase().endsWith(".xml");
|
||||
}
|
||||
}
|
@ -0,0 +1,532 @@
|
||||
/*
|
||||
* 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.object;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.model.L2Object;
|
||||
|
||||
/**
|
||||
* This class is a highly optimized hashtable, where keys are integers. The main goal of this class is to allow concurent read/iterate and write access to this table, plus minimal used memory. This class uses plain array as the table of values, and keys are used to get position in the table. If the
|
||||
* position is already busy, we iterate to the next position, unil we find the needed element or null. To iterate over the table (read access) we may simply iterate throgh table array. In case we remove an element from the table, we check - if the next position is null, we reset table's slot to
|
||||
* null, otherwice we assign it to a dummy value
|
||||
* @author mkizub
|
||||
* @param <T> type of values stored in this hashtable
|
||||
*/
|
||||
public final class L2ObjectHashMap<T extends L2Object>extends L2ObjectMap<T>
|
||||
{
|
||||
protected static final Logger LOGGER = Logger.getLogger(L2ObjectHashMap.class.getName());
|
||||
|
||||
private static final boolean TRACE = false;
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final int[] PRIMES =
|
||||
{
|
||||
5,
|
||||
7,
|
||||
11,
|
||||
17,
|
||||
23,
|
||||
29,
|
||||
37,
|
||||
47,
|
||||
59,
|
||||
71,
|
||||
89,
|
||||
107,
|
||||
131,
|
||||
163,
|
||||
197,
|
||||
239,
|
||||
293,
|
||||
353,
|
||||
431,
|
||||
521,
|
||||
631,
|
||||
761,
|
||||
919,
|
||||
1103,
|
||||
1327,
|
||||
1597,
|
||||
1931,
|
||||
2333,
|
||||
2801,
|
||||
3371,
|
||||
4049,
|
||||
4861,
|
||||
5839,
|
||||
7013,
|
||||
8419,
|
||||
10103,
|
||||
12143,
|
||||
14591,
|
||||
17519,
|
||||
21023,
|
||||
25229,
|
||||
30293,
|
||||
36353,
|
||||
43627,
|
||||
52361,
|
||||
62851,
|
||||
75431,
|
||||
90523,
|
||||
108631,
|
||||
130363,
|
||||
156437,
|
||||
187751,
|
||||
225307,
|
||||
270371,
|
||||
324449,
|
||||
389357,
|
||||
467237,
|
||||
560689,
|
||||
672827,
|
||||
807403,
|
||||
968897,
|
||||
1162687,
|
||||
1395263,
|
||||
1674319,
|
||||
2009191,
|
||||
2411033,
|
||||
2893249,
|
||||
3471899,
|
||||
4166287,
|
||||
4999559,
|
||||
5999471,
|
||||
7199369
|
||||
};
|
||||
|
||||
private T[] _table;
|
||||
private int[] _keys;
|
||||
private int _count;
|
||||
|
||||
private static int getPrime(int min)
|
||||
{
|
||||
for (int element : PRIMES)
|
||||
{
|
||||
if (element >= min)
|
||||
{
|
||||
return element;
|
||||
}
|
||||
}
|
||||
throw new OutOfMemoryError();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public L2ObjectHashMap()
|
||||
{
|
||||
final int size = PRIMES[0];
|
||||
_table = (T[]) new L2Object[size];
|
||||
_keys = new int[size];
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#size()
|
||||
*/
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#isEmpty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return _count == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#clear()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized void clear()
|
||||
{
|
||||
final int size = PRIMES[0];
|
||||
_table = (T[]) new L2Object[size];
|
||||
_keys = new int[size];
|
||||
_count = 0;
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
}
|
||||
|
||||
private void check()
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
int cnt = 0;
|
||||
for (int i = 0; i < _table.length; i++)
|
||||
{
|
||||
final L2Object obj = _table[i];
|
||||
if (obj == null)
|
||||
{
|
||||
assert (_keys[i] == 0) || (_keys[i] == 0x80000000);
|
||||
}
|
||||
else
|
||||
{
|
||||
cnt++;
|
||||
assert obj.getObjectId() == (_keys[i] & 0x7FFFFFFF);
|
||||
}
|
||||
}
|
||||
assert cnt == _count;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#put(T)
|
||||
*/
|
||||
@Override
|
||||
public synchronized void put(T obj)
|
||||
{
|
||||
if (_count >= (_table.length / 2))
|
||||
{
|
||||
expand();
|
||||
}
|
||||
final int hashcode = obj.getObjectId();
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert hashcode > 0;
|
||||
}
|
||||
int seed = hashcode;
|
||||
final int incr = 1 + (((seed >> 5) + 1) % (_table.length - 1));
|
||||
int ntry = 0;
|
||||
int slot = -1; // keep last found slot
|
||||
do
|
||||
{
|
||||
final int pos = (seed % _table.length) & 0x7FFFFFFF;
|
||||
if (_table[pos] == null)
|
||||
{
|
||||
if (slot < 0)
|
||||
{
|
||||
slot = pos;
|
||||
}
|
||||
if (_keys[pos] >= 0)
|
||||
{
|
||||
// found an empty slot without previous collisions,
|
||||
// but use previously found slot
|
||||
_keys[slot] = hashcode;
|
||||
_table[slot] = obj;
|
||||
_count++;
|
||||
if (TRACE)
|
||||
{
|
||||
LOGGER.warning("ht: put obj id=" + hashcode + " at slot=" + slot);
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if we are adding the same object
|
||||
if (_table[pos] == obj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// this should never happen
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert obj.getObjectId() != _table[pos].getObjectId();
|
||||
}
|
||||
// if there was no collisions at this slot, and we found a free
|
||||
// slot previously - use found slot
|
||||
if ((slot >= 0) && (_keys[pos] > 0))
|
||||
{
|
||||
_keys[slot] |= hashcode; // preserve collision bit
|
||||
_table[slot] = obj;
|
||||
_count++;
|
||||
if (TRACE)
|
||||
{
|
||||
LOGGER.warning("ht: put obj id=" + hashcode + " at slot=" + slot);
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// set collision bit
|
||||
_keys[pos] |= 0x80000000;
|
||||
// calculate next slot
|
||||
seed += incr;
|
||||
}
|
||||
while (++ntry < _table.length);
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#remove(T)
|
||||
*/
|
||||
@Override
|
||||
public synchronized void remove(T obj)
|
||||
{
|
||||
final int hashcode = obj.getObjectId();
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert hashcode > 0;
|
||||
}
|
||||
int seed = hashcode;
|
||||
final int incr = 1 + (((seed >> 5) + 1) % (_table.length - 1));
|
||||
int ntry = 0;
|
||||
do
|
||||
{
|
||||
final int pos = (seed % _table.length) & 0x7FFFFFFF;
|
||||
if (_table[pos] == obj)
|
||||
{
|
||||
// found the object
|
||||
_keys[pos] &= 0x80000000; // preserve collision bit
|
||||
_table[pos] = null;
|
||||
_count--;
|
||||
if (TRACE)
|
||||
{
|
||||
LOGGER.warning("ht: remove obj id=" + hashcode + " from slot=" + pos);
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// check for collision (if we previously deleted element)
|
||||
if ((_table[pos] == null) && (_keys[pos] >= 0))
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
return; // throw new IllegalArgumentException();
|
||||
}
|
||||
// calculate next slot
|
||||
seed += incr;
|
||||
}
|
||||
while (++ntry < _table.length);
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#get(int)
|
||||
*/
|
||||
@Override
|
||||
public T get(int id)
|
||||
{
|
||||
final int size = _table.length;
|
||||
if (id <= 0)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
if (size <= 11)
|
||||
{
|
||||
// for small tables linear check is fast
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
if ((_keys[i] & 0x7FFFFFFF) == id)
|
||||
{
|
||||
return _table[i];
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
int seed = id;
|
||||
final int incr = 1 + (((seed >> 5) + 1) % (size - 1));
|
||||
int ntry = 0;
|
||||
do
|
||||
{
|
||||
final int pos = (seed % size) & 0x7FFFFFFF;
|
||||
if ((_keys[pos] & 0x7FFFFFFF) == id)
|
||||
{
|
||||
return _table[pos];
|
||||
}
|
||||
// check for collision (if we previously deleted element)
|
||||
if ((_table[pos] == null) && (_keys[pos] >= 0))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
// calculate next slot
|
||||
seed += incr;
|
||||
}
|
||||
while (++ntry < size);
|
||||
return null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#contains(T)
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(T obj)
|
||||
{
|
||||
return get(obj.getObjectId()) != null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private/* already synchronized in put() */void expand()
|
||||
{
|
||||
final int newSize = getPrime(_table.length + 1);
|
||||
final L2Object[] newTable = new L2Object[newSize];
|
||||
final int[] newKeys = new int[newSize];
|
||||
|
||||
// over all old entries
|
||||
next_entry: for (int i = 0; i < _table.length; i++)
|
||||
{
|
||||
final L2Object obj = _table[i];
|
||||
if (obj == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
final int hashcode = _keys[i] & 0x7FFFFFFF;
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert hashcode == obj.getObjectId();
|
||||
}
|
||||
int seed = hashcode;
|
||||
final int incr = 1 + (((seed >> 5) + 1) % (newSize - 1));
|
||||
int ntry = 0;
|
||||
do
|
||||
{
|
||||
final int pos = (seed % newSize) & 0x7FFFFFFF;
|
||||
if (newTable[pos] == null)
|
||||
{
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert (newKeys[pos] == 0) && (hashcode != 0);
|
||||
}
|
||||
// found an empty slot without previous collisions,
|
||||
// but use previously found slot
|
||||
newKeys[pos] = hashcode;
|
||||
newTable[pos] = obj;
|
||||
if (TRACE)
|
||||
{
|
||||
LOGGER.warning("ht: move obj id=" + hashcode + " from slot=" + i + " to slot=" + pos);
|
||||
}
|
||||
continue next_entry;
|
||||
}
|
||||
// set collision bit
|
||||
newKeys[pos] |= 0x80000000;
|
||||
// calculate next slot
|
||||
seed += incr;
|
||||
}
|
||||
while (++ntry < newSize);
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
_table = (T[]) newTable;
|
||||
_keys = newKeys;
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
return new Itr(_table);
|
||||
}
|
||||
|
||||
class Itr implements Iterator<T>
|
||||
{
|
||||
private final T[] _array;
|
||||
private int _nextIdx;
|
||||
private T _nextObj;
|
||||
private T _lastRet;
|
||||
|
||||
Itr(T[] pArray)
|
||||
{
|
||||
this._array = pArray;
|
||||
for (; _nextIdx < _array.length; _nextIdx++)
|
||||
{
|
||||
_nextObj = _array[_nextIdx];
|
||||
if (_nextObj != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return _nextObj != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next()
|
||||
{
|
||||
if (_nextObj == null)
|
||||
{
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
_lastRet = _nextObj;
|
||||
for (_nextIdx++; _nextIdx < _array.length; _nextIdx++)
|
||||
{
|
||||
_nextObj = _array[_nextIdx];
|
||||
if (_nextObj != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_nextIdx >= _array.length)
|
||||
{
|
||||
_nextObj = null;
|
||||
}
|
||||
return _lastRet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove()
|
||||
{
|
||||
if (_lastRet == null)
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
L2ObjectHashMap.this.remove(_lastRet);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,522 @@
|
||||
/*
|
||||
* 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.object;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.NoSuchElementException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.model.L2Object;
|
||||
|
||||
/**
|
||||
* This class is a highly optimized hashtable, where keys are integers. The main goal of this class is to allow concurent read/iterate and write access to this table, plus minimal used memory. This class uses plain array as the table of values, and keys are used to get position in the table. If the
|
||||
* position is already busy, we iterate to the next position, unil we find the needed element or null. To iterate over the table (read access) we may simply iterate throgh table array. In case we remove an element from the table, we check - if the next position is null, we reset table's slot to
|
||||
* null, otherwice we assign it to a dummy value
|
||||
* @author mkizub
|
||||
* @param <T> type of values stored in this hashtable
|
||||
*/
|
||||
public final class L2ObjectHashSet<T extends L2Object>extends L2ObjectSet<T>
|
||||
{
|
||||
protected static final Logger LOGGER = Logger.getLogger(L2ObjectHashSet.class.getName());
|
||||
|
||||
private static final boolean TRACE = false;
|
||||
private static final boolean DEBUG = Config.DEBUG;
|
||||
|
||||
private static final int[] PRIMES =
|
||||
{
|
||||
5,
|
||||
7,
|
||||
11,
|
||||
17,
|
||||
23,
|
||||
29,
|
||||
37,
|
||||
47,
|
||||
59,
|
||||
71,
|
||||
89,
|
||||
107,
|
||||
131,
|
||||
163,
|
||||
197,
|
||||
239,
|
||||
293,
|
||||
353,
|
||||
431,
|
||||
521,
|
||||
631,
|
||||
761,
|
||||
919,
|
||||
1103,
|
||||
1327,
|
||||
1597,
|
||||
1931,
|
||||
2333,
|
||||
2801,
|
||||
3371,
|
||||
4049,
|
||||
4861,
|
||||
5839,
|
||||
7013,
|
||||
8419,
|
||||
10103,
|
||||
12143,
|
||||
14591,
|
||||
17519,
|
||||
21023,
|
||||
25229,
|
||||
30293,
|
||||
36353,
|
||||
43627,
|
||||
52361,
|
||||
62851,
|
||||
75431,
|
||||
90523,
|
||||
108631,
|
||||
130363,
|
||||
156437,
|
||||
187751,
|
||||
225307,
|
||||
270371,
|
||||
324449,
|
||||
389357,
|
||||
467237,
|
||||
560689,
|
||||
672827,
|
||||
807403,
|
||||
968897,
|
||||
1162687,
|
||||
1395263,
|
||||
1674319,
|
||||
2009191,
|
||||
2411033,
|
||||
2893249,
|
||||
3471899,
|
||||
4166287,
|
||||
4999559,
|
||||
5999471,
|
||||
7199369
|
||||
};
|
||||
|
||||
private T[] _table;
|
||||
private int[] _collisions;
|
||||
private int _count;
|
||||
|
||||
private static int getPrime(int min)
|
||||
{
|
||||
for (int element : PRIMES)
|
||||
{
|
||||
if (element >= min)
|
||||
{
|
||||
return element;
|
||||
}
|
||||
}
|
||||
throw new OutOfMemoryError();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public L2ObjectHashSet()
|
||||
{
|
||||
final int size = PRIMES[0];
|
||||
_table = (T[]) new L2Object[size];
|
||||
_collisions = new int[(size + 31) >> 5];
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#size()
|
||||
*/
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return _count;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#isEmpty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return _count == 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#clear()
|
||||
*/
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public synchronized void clear()
|
||||
{
|
||||
final int size = PRIMES[0];
|
||||
_table = (T[]) new L2Object[size];
|
||||
_collisions = new int[(size + 31) >> 5];
|
||||
_count = 0;
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
}
|
||||
|
||||
private void check()
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
int cnt = 0;
|
||||
assert _collisions.length == ((_table.length + 31) >> 5);
|
||||
for (T obj : _table)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
cnt++;
|
||||
}
|
||||
}
|
||||
assert cnt == _count;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#put(T)
|
||||
*/
|
||||
@Override
|
||||
public synchronized void put(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (contains(obj))
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (_count >= (_table.length / 2))
|
||||
{
|
||||
expand();
|
||||
}
|
||||
final int hashcode = obj.getObjectId();
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert hashcode > 0;
|
||||
}
|
||||
int seed = hashcode;
|
||||
final int incr = 1 + (((seed >> 5) + 1) % (_table.length - 1));
|
||||
int ntry = 0;
|
||||
int slot = -1; // keep last found slot
|
||||
do
|
||||
{
|
||||
final int pos = (seed % _table.length) & 0x7FFFFFFF;
|
||||
if (_table[pos] == null)
|
||||
{
|
||||
if (slot < 0)
|
||||
{
|
||||
slot = pos;
|
||||
}
|
||||
if ((_collisions[pos >> 5] & (1 << (pos & 31))) == 0)
|
||||
{
|
||||
// found an empty slot without previous collisions,
|
||||
// but use previously found slot
|
||||
_table[slot] = obj;
|
||||
_count++;
|
||||
if (TRACE)
|
||||
{
|
||||
LOGGER.warning("ht: put obj id=" + hashcode + " at slot=" + slot);
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// check if we are adding the same object
|
||||
if (_table[pos] == obj)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// this should never happen
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert obj.getObjectId() != _table[pos].getObjectId();
|
||||
}
|
||||
// if there was no collisions at this slot, and we found a free
|
||||
// slot previously - use found slot
|
||||
if ((slot >= 0) && ((_collisions[pos >> 5] & (1 << (pos & 31))) == 0))
|
||||
{
|
||||
_table[slot] = obj;
|
||||
_count++;
|
||||
if (TRACE)
|
||||
{
|
||||
LOGGER.warning("ht: put obj id=" + hashcode + " at slot=" + slot);
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// set collision bit
|
||||
_collisions[pos >> 5] |= 1 << (pos & 31);
|
||||
// calculate next slot
|
||||
seed += incr;
|
||||
}
|
||||
while (++ntry < _table.length);
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#remove(T)
|
||||
*/
|
||||
@Override
|
||||
public synchronized void remove(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!contains(obj))
|
||||
{
|
||||
return;
|
||||
}
|
||||
final int hashcode = obj.getObjectId();
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert hashcode > 0;
|
||||
}
|
||||
int seed = hashcode;
|
||||
final int incr = 1 + (((seed >> 5) + 1) % (_table.length - 1));
|
||||
int ntry = 0;
|
||||
do
|
||||
{
|
||||
final int pos = (seed % _table.length) & 0x7FFFFFFF;
|
||||
if (_table[pos] == obj)
|
||||
{
|
||||
// found the object
|
||||
_table[pos] = null;
|
||||
_count--;
|
||||
if (TRACE)
|
||||
{
|
||||
LOGGER.warning("ht: remove obj id=" + hashcode + " from slot=" + pos);
|
||||
}
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
return;
|
||||
}
|
||||
// check for collision (if we previously deleted element)
|
||||
if ((_table[pos] == null) && ((_collisions[pos >> 5] & (1 << (pos & 31))) == 0))
|
||||
{
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
return; // throw new IllegalArgumentException();
|
||||
}
|
||||
// calculate next slot
|
||||
seed += incr;
|
||||
}
|
||||
while (++ntry < _table.length);
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#contains(T)
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(T obj)
|
||||
{
|
||||
final int size = _table.length;
|
||||
if (size <= 11)
|
||||
{
|
||||
// for small tables linear check is fast
|
||||
for (T a_table : _table)
|
||||
{
|
||||
if (a_table == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
final int hashcode = obj.getObjectId();
|
||||
if (Config.ASSERT)
|
||||
{
|
||||
assert hashcode > 0;
|
||||
}
|
||||
int seed = hashcode;
|
||||
final int incr = 1 + (((seed >> 5) + 1) % (size - 1));
|
||||
int ntry = 0;
|
||||
do
|
||||
{
|
||||
final int pos = (seed % size) & 0x7FFFFFFF;
|
||||
if (_table[pos] == obj)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// check for collision (if we previously deleted element)
|
||||
if ((_table[pos] == null) && ((_collisions[pos >> 5] & (1 << (pos & 31))) == 0))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// calculate next slot
|
||||
seed += incr;
|
||||
}
|
||||
while (++ntry < size);
|
||||
return false;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private/* already synchronized in put() */void expand()
|
||||
{
|
||||
final int newSize = getPrime(_table.length + 1);
|
||||
final L2Object[] newTable = new L2Object[newSize];
|
||||
final int[] newCollisions = new int[(newSize + 31) >> 5];
|
||||
|
||||
// over all old entries
|
||||
next_entry: for (int i = 0; i < _table.length; i++)
|
||||
{
|
||||
final L2Object obj = _table[i];
|
||||
if (obj == null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
final int hashcode = obj.getObjectId();
|
||||
int seed = hashcode;
|
||||
final int incr = 1 + (((seed >> 5) + 1) % (newSize - 1));
|
||||
int ntry = 0;
|
||||
do
|
||||
{
|
||||
final int pos = (seed % newSize) & 0x7FFFFFFF;
|
||||
if (newTable[pos] == null)
|
||||
{
|
||||
// found an empty slot without previous collisions,
|
||||
// but use previously found slot
|
||||
newTable[pos] = obj;
|
||||
if (TRACE)
|
||||
{
|
||||
LOGGER.warning("ht: move obj id=" + hashcode + " from slot=" + i + " to slot=" + pos);
|
||||
}
|
||||
continue next_entry;
|
||||
}
|
||||
// set collision bit
|
||||
newCollisions[pos >> 5] |= 1 << (pos & 31);
|
||||
// calculate next slot
|
||||
seed += incr;
|
||||
}
|
||||
while (++ntry < newSize);
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
_table = (T[]) newTable;
|
||||
_collisions = newCollisions;
|
||||
if (DEBUG)
|
||||
{
|
||||
check();
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
return new Itr(_table);
|
||||
}
|
||||
|
||||
class Itr implements Iterator<T>
|
||||
{
|
||||
private final T[] _array;
|
||||
private int _nextIdx;
|
||||
private T _nextObj;
|
||||
private T _lastRet;
|
||||
|
||||
Itr(T[] pArray)
|
||||
{
|
||||
this._array = pArray;
|
||||
for (; _nextIdx < _array.length; _nextIdx++)
|
||||
{
|
||||
_nextObj = _array[_nextIdx];
|
||||
if (_nextObj != null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext()
|
||||
{
|
||||
return _nextObj != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T next()
|
||||
{
|
||||
if (_nextObj == null)
|
||||
{
|
||||
throw new NoSuchElementException();
|
||||
}
|
||||
_lastRet = _nextObj;
|
||||
for (_nextIdx++; _nextIdx < _array.length; _nextIdx++)
|
||||
{
|
||||
_nextObj = _array[_nextIdx];
|
||||
if (_nextObj != null)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (_nextIdx >= _array.length)
|
||||
{
|
||||
_nextObj = null;
|
||||
}
|
||||
return _lastRet;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove()
|
||||
{
|
||||
if (_lastRet == null)
|
||||
{
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
L2ObjectHashSet.this.remove(_lastRet);
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* 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.object;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.model.L2Object;
|
||||
|
||||
/**
|
||||
* This class ...
|
||||
* @author luisantonioa
|
||||
* @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class L2ObjectMap<T extends L2Object> implements Iterable<T>
|
||||
{
|
||||
public abstract int size();
|
||||
|
||||
public abstract boolean isEmpty();
|
||||
|
||||
public abstract void clear();
|
||||
|
||||
public abstract void put(T obj);
|
||||
|
||||
public abstract void remove(T obj);
|
||||
|
||||
public abstract T get(int id);
|
||||
|
||||
public abstract boolean contains(T obj);
|
||||
|
||||
@Override
|
||||
public abstract Iterator<T> iterator();
|
||||
|
||||
public static L2ObjectMap<L2Object> createL2ObjectMap()
|
||||
{
|
||||
switch (Config.MAP_TYPE)
|
||||
{
|
||||
case WorldObjectMap:
|
||||
{
|
||||
return new WorldObjectMap<>();
|
||||
}
|
||||
default:
|
||||
{
|
||||
return new WorldObjectTree<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,77 @@
|
||||
/*
|
||||
* 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.object;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.l2jmobius.Config;
|
||||
import com.l2jmobius.gameserver.model.L2Object;
|
||||
import com.l2jmobius.gameserver.model.actor.L2Playable;
|
||||
|
||||
/**
|
||||
* This class ...
|
||||
* @author luisantonioa
|
||||
* @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
|
||||
* @param <T>
|
||||
*/
|
||||
public abstract class L2ObjectSet<T extends L2Object> implements Iterable<T>
|
||||
{
|
||||
public static L2ObjectSet<L2Object> createL2ObjectSet()
|
||||
{
|
||||
switch (Config.SET_TYPE)
|
||||
{
|
||||
case WorldObjectSet:
|
||||
{
|
||||
return new WorldObjectSet<>();
|
||||
}
|
||||
default:
|
||||
{
|
||||
return new L2ObjectHashSet<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static L2ObjectSet<L2Playable> createL2PlayerSet()
|
||||
{
|
||||
switch (Config.SET_TYPE)
|
||||
{
|
||||
case WorldObjectSet:
|
||||
{
|
||||
return new WorldObjectSet<>();
|
||||
}
|
||||
default:
|
||||
{
|
||||
return new L2ObjectHashSet<>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract int size();
|
||||
|
||||
public abstract boolean isEmpty();
|
||||
|
||||
public abstract void clear();
|
||||
|
||||
public abstract void put(T obj);
|
||||
|
||||
public abstract void remove(T obj);
|
||||
|
||||
public abstract boolean contains(T obj);
|
||||
|
||||
@Override
|
||||
public abstract Iterator<T> iterator();
|
||||
}
|
@ -0,0 +1,124 @@
|
||||
/*
|
||||
* 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.object;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.l2jmobius.gameserver.model.L2Object;
|
||||
|
||||
/**
|
||||
* This class ...
|
||||
* @author luisantonioa
|
||||
* @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
|
||||
* @param <T>
|
||||
*/
|
||||
public class WorldObjectMap<T extends L2Object>extends L2ObjectMap<T>
|
||||
{
|
||||
Map<Integer, T> _objectMap = new ConcurrentHashMap<>();
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#size()
|
||||
*/
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return _objectMap.size();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#isEmpty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return _objectMap.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
_objectMap.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#put(T)
|
||||
*/
|
||||
@Override
|
||||
public void put(T obj)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
_objectMap.put(obj.getObjectId(), obj);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#remove(T)
|
||||
*/
|
||||
@Override
|
||||
public void remove(T obj)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
_objectMap.remove(obj.getObjectId());
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#get(int)
|
||||
*/
|
||||
@Override
|
||||
public T get(int id)
|
||||
{
|
||||
return _objectMap.get(id);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#contains(T)
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return _objectMap.get(obj.getObjectId()) != null;
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectMap#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
return _objectMap.values().iterator();
|
||||
}
|
||||
}
|
@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.object;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.l2jmobius.gameserver.model.L2Object;
|
||||
|
||||
/**
|
||||
* This class ...
|
||||
* @version $Revision: 1.2 $ $Date: 2004/06/27 08:12:59 $
|
||||
* @param <T>
|
||||
*/
|
||||
public class WorldObjectSet<T extends L2Object>extends L2ObjectSet<T>
|
||||
{
|
||||
private final Map<Integer, T> _objectMap;
|
||||
|
||||
public WorldObjectSet()
|
||||
{
|
||||
_objectMap = new ConcurrentHashMap<>();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#size()
|
||||
*/
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
return _objectMap.size();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#isEmpty()
|
||||
*/
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
return _objectMap.isEmpty();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#clear()
|
||||
*/
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
_objectMap.clear();
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#put(T)
|
||||
*/
|
||||
@Override
|
||||
public void put(T obj)
|
||||
{
|
||||
_objectMap.put(obj.getObjectId(), obj);
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#remove(T)
|
||||
*/
|
||||
@Override
|
||||
public void remove(T obj)
|
||||
{
|
||||
_objectMap.remove(obj.getObjectId());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#contains(T)
|
||||
*/
|
||||
@Override
|
||||
public boolean contains(T obj)
|
||||
{
|
||||
return _objectMap.containsKey(obj.getObjectId());
|
||||
}
|
||||
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
* @see com.l2jmobius.util.L2ObjectSet#iterator()
|
||||
*/
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
return _objectMap.values().iterator();
|
||||
}
|
||||
}
|
@ -0,0 +1,158 @@
|
||||
/*
|
||||
* 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.object;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeMap;
|
||||
import java.util.concurrent.locks.Lock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
||||
import com.l2jmobius.gameserver.model.L2Object;
|
||||
|
||||
/**
|
||||
* @author dishkols
|
||||
* @param <T>
|
||||
*/
|
||||
public class WorldObjectTree<T extends L2Object>extends L2ObjectMap<T>
|
||||
{
|
||||
private final TreeMap<Integer, T> _objectMap = new TreeMap<>();
|
||||
private final ReentrantReadWriteLock _rwl = new ReentrantReadWriteLock();
|
||||
private final Lock _r = _rwl.readLock();
|
||||
private final Lock _w = _rwl.writeLock();
|
||||
|
||||
@Override
|
||||
public int size()
|
||||
{
|
||||
_r.lock();
|
||||
try
|
||||
{
|
||||
return _objectMap.size();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEmpty()
|
||||
{
|
||||
_r.lock();
|
||||
try
|
||||
{
|
||||
return _objectMap.isEmpty();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void clear()
|
||||
{
|
||||
_w.lock();
|
||||
try
|
||||
{
|
||||
_objectMap.clear();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_w.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void put(T obj)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
_w.lock();
|
||||
try
|
||||
{
|
||||
_objectMap.put(obj.getObjectId(), obj);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_w.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove(T obj)
|
||||
{
|
||||
if (obj != null)
|
||||
{
|
||||
_w.lock();
|
||||
try
|
||||
{
|
||||
_objectMap.remove(obj.getObjectId());
|
||||
}
|
||||
finally
|
||||
{
|
||||
_w.unlock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public T get(int id)
|
||||
{
|
||||
_r.lock();
|
||||
try
|
||||
{
|
||||
return _objectMap.get(id);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(T obj)
|
||||
{
|
||||
if (obj == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_r.lock();
|
||||
try
|
||||
{
|
||||
return _objectMap.containsValue(obj);
|
||||
}
|
||||
finally
|
||||
{
|
||||
_r.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<T> iterator()
|
||||
{
|
||||
_r.lock();
|
||||
try
|
||||
{
|
||||
return _objectMap.values().iterator();
|
||||
}
|
||||
finally
|
||||
{
|
||||
_r.unlock();
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user