Interlude branch.

This commit is contained in:
MobiusDev
2018-03-03 00:51:38 +00:00
parent ae7660220a
commit e013196428
17475 changed files with 1039393 additions and 0 deletions

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,30 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius;
/**
* This class used to be the starter class, since LS/GS split, it only retains server mode
*/
public class Server
{
// constants for the server mode
public static final int MODE_NONE = 0;
public static final int MODE_GAMESERVER = 1;
public static final int MODE_LOGINSERVER = 2;
public static int serverMode = MODE_NONE;
}

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

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

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

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

View File

@ -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;
}
}
}

View File

@ -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.");
}
}
}

View 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;
}
}
}

View File

@ -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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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
}
}
}

View File

@ -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();
}

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -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;
}
}
}
}

View File

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

View File

@ -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;
}
}

View File

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

View File

@ -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;
}
}

View File

@ -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>&gt;= desiredCapacity</code> and very close to <code>desiredCapacity</code> (within 11% if <code>desiredCapacity &gt;= 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];
}
}

View 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)];
}
}

View File

@ -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, &quot;text 1&quot;, String.valueOf(npcId));
* StringUtil.append(&quot;text 2&quot;);
* </pre>
*
* Good:
*
* <pre>
* final StringBuilder sbString = new StringBuilder();
* StringUtil.append(sbString, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
* </pre>
*
* Why?<br/>
* Because the less calls you do, the less memory re-allocations have to be done 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, &quot;header start&quot;, someText, &quot;header end&quot;);
* for (int i = 0; i &lt; 50; i++)
* {
* StringUtil.append(sbString, &quot;text 1&quot;, stringArray[i], &quot;text 2&quot;);
* }
* </pre>
*
* Good:
*
* <pre>
* final StringBuilder sbString = StringUtil.startAppend(1300, &quot;header start&quot;, someText, &quot;header end&quot;);
* for (int i = 0; i &lt; 50; i++)
* {
* StringUtil.append(sbString, &quot;text 1&quot;, stringArray[i], &quot;text 2&quot;);
* }
* </pre>
*
* Why?<br/>
* When using StringUtil.append(), memory is only allocated to fit in the strings in method argument. So on each loop new memory for the string has to be allocated and old string has to be copied to the new string. With size hint, even if the size hint is above the needed memory, memory is saved
* because new memory has not to be allocated on each cycle. Also it is much faster if no string copy tasks has to be performed. So if concatenating strings in a loop, count approximately the size and set it as the hint for the string builder size. It's better to make the size hint little bit larger
* rather than smaller.<br/>
* In case there is no text appended before the cycle, just use <code>new
* StringBuilder(1300)</code>.
* <h2>Concatenation and Constants</h2> Bad:
*
* <pre>
* StringUtil.concat(&quot;text 1 &quot;, &quot;text 2&quot;, String.valueOf(npcId));
* </pre>
*
* Good:
*
* <pre>
* StringUtil.concat(&quot;text 1 &quot; + &quot;text 2&quot;, String.valueOf(npcId));
* </pre>
*
* or
*
* <pre>
* StringUtil.concat(&quot;text 1 text 2&quot;, String.valueOf(npcId));
* </pre>
*
* Why?<br/>
* It saves some cycles when determining size of memory that needs to be allocated because less strings are passed to concat() method. But do not use + for concatenation of non-constant strings, that degrades performance and makes extra memory allocations needed.
* <h2>Concatenation and Constant Variables</h2> Bad:
*
* <pre>
* String glue = &quot;some glue&quot;;
* StringUtil.concat(&quot;text 1&quot;, glue, &quot;text 2&quot;, glue, String.valueOf(npcId));
* </pre>
*
* Good:
*
* <pre>
* final String glue = &quot;some glue&quot;;
* StringUtil.concat(&quot;text 1&quot; + glue + &quot;text2&quot; + glue, String.valueOf(npcId));
* </pre>
*
* Why? Because when using <code>final</code> keyword, the <code>glue</code> is marked as constant string and compiler treats it as a constant string so it is able to create string "text1some gluetext2some glue" during the compilation. But this only works in case the value is known at compilation
* time, so this cannot be used for cases like <code>final String objectIdString =
* String.valueOf(getObjectId)</code>.
* <h2>StringBuilder Reuse</h2> Bad:
*
* <pre>
* final StringBuilder sbString1 = new StringBuilder();
* StringUtil.append(sbString1, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
* ... // output of sbString1, it is no more needed
* final StringBuilder sbString2 = new StringBuilder();
* StringUtil.append(sbString2, &quot;text 3&quot;, String.valueOf(npcId), &quot;text 4&quot;);
* </pre>
*
* Good:
*
* <pre>
* final StringBuilder sbString = new StringBuilder();
* StringUtil.append(sbString, &quot;text 1&quot;, String.valueOf(npcId), &quot;text 2&quot;);
* ... // output of sbString, it is no more needed
* sbString.setLength(0);
* StringUtil.append(sbString, &quot;text 3&quot;, String.valueOf(npcId), &quot;text 4&quot;);
* </pre>
*
* Why?</br>
* In first case, new memory has to be allocated for the second string. In second case already allocated memory is reused, but only in case the new string is not longer than the previously allocated string. Anyway, the second way is better because the string either fits in the memory and some memory
* is saved, or it does not fit in the memory, and in that case it works as in the first case.
* <h2>Primitives to Strings</h2> To convert primitives to string, use String.valueOf().
* <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();
}
}

View File

@ -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();
}
}

View File

@ -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;
}
}

View File

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

View File

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

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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<>();
}
}
}
}

View File

@ -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();
}

View File

@ -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();
}
}

View File

@ -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();
}
}

View File

@ -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();
}
}
}

View File

@ -0,0 +1,652 @@
/*
* 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.gameserver;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Calendar;
import java.util.logging.LogManager;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.Server;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.commons.crypt.nProtect;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.commons.mmocore.NetcoreConfig;
import com.l2jmobius.commons.mmocore.SelectorConfig;
import com.l2jmobius.commons.mmocore.SelectorThread;
import com.l2jmobius.commons.util.IPv4Filter;
import com.l2jmobius.commons.util.Memory;
import com.l2jmobius.commons.util.Util;
import com.l2jmobius.gameserver.cache.CrestCache;
import com.l2jmobius.gameserver.cache.HtmCache;
import com.l2jmobius.gameserver.communitybbs.Manager.ForumsBBSManager;
import com.l2jmobius.gameserver.datatables.BufferTable;
import com.l2jmobius.gameserver.datatables.GmListTable;
import com.l2jmobius.gameserver.datatables.HeroSkillTable;
import com.l2jmobius.gameserver.datatables.NobleSkillTable;
import com.l2jmobius.gameserver.datatables.OfflineTradeTable;
import com.l2jmobius.gameserver.datatables.SkillTable;
import com.l2jmobius.gameserver.datatables.csv.DoorTable;
import com.l2jmobius.gameserver.datatables.csv.ExtractableItemsData;
import com.l2jmobius.gameserver.datatables.csv.FishTable;
import com.l2jmobius.gameserver.datatables.csv.HennaTable;
import com.l2jmobius.gameserver.datatables.csv.MapRegionTable;
import com.l2jmobius.gameserver.datatables.csv.NpcWalkerRoutesTable;
import com.l2jmobius.gameserver.datatables.csv.RecipeTable;
import com.l2jmobius.gameserver.datatables.csv.StaticObjects;
import com.l2jmobius.gameserver.datatables.csv.SummonItemsData;
import com.l2jmobius.gameserver.datatables.sql.AccessLevels;
import com.l2jmobius.gameserver.datatables.sql.AdminCommandAccessRights;
import com.l2jmobius.gameserver.datatables.sql.ArmorSetsTable;
import com.l2jmobius.gameserver.datatables.sql.CharNameTable;
import com.l2jmobius.gameserver.datatables.sql.CharTemplateTable;
import com.l2jmobius.gameserver.datatables.sql.ClanTable;
import com.l2jmobius.gameserver.datatables.sql.CustomArmorSetsTable;
import com.l2jmobius.gameserver.datatables.sql.HelperBuffTable;
import com.l2jmobius.gameserver.datatables.sql.HennaTreeTable;
import com.l2jmobius.gameserver.datatables.sql.L2PetDataTable;
import com.l2jmobius.gameserver.datatables.sql.LevelUpData;
import com.l2jmobius.gameserver.datatables.sql.NpcTable;
import com.l2jmobius.gameserver.datatables.sql.SkillSpellbookTable;
import com.l2jmobius.gameserver.datatables.sql.SkillTreeTable;
import com.l2jmobius.gameserver.datatables.sql.SpawnTable;
import com.l2jmobius.gameserver.datatables.sql.TeleportLocationTable;
import com.l2jmobius.gameserver.datatables.xml.AugmentationData;
import com.l2jmobius.gameserver.datatables.xml.ExperienceData;
import com.l2jmobius.gameserver.datatables.xml.ItemTable;
import com.l2jmobius.gameserver.datatables.xml.ZoneData;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.geodata.geoeditorcon.GeoEditorListener;
import com.l2jmobius.gameserver.geodata.pathfinding.PathFinding;
import com.l2jmobius.gameserver.handler.AdminCommandHandler;
import com.l2jmobius.gameserver.handler.AutoAnnouncementHandler;
import com.l2jmobius.gameserver.handler.AutoChatHandler;
import com.l2jmobius.gameserver.handler.ItemHandler;
import com.l2jmobius.gameserver.handler.SkillHandler;
import com.l2jmobius.gameserver.handler.UserCommandHandler;
import com.l2jmobius.gameserver.handler.VoicedCommandHandler;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.instancemanager.AuctionManager;
import com.l2jmobius.gameserver.instancemanager.AutoSaveManager;
import com.l2jmobius.gameserver.instancemanager.AwayManager;
import com.l2jmobius.gameserver.instancemanager.BoatManager;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.CastleManorManager;
import com.l2jmobius.gameserver.instancemanager.ClanHallManager;
import com.l2jmobius.gameserver.instancemanager.ClassDamageManager;
import com.l2jmobius.gameserver.instancemanager.CoupleManager;
import com.l2jmobius.gameserver.instancemanager.CrownManager;
import com.l2jmobius.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jmobius.gameserver.instancemanager.DayNightSpawnManager;
import com.l2jmobius.gameserver.instancemanager.DimensionalRiftManager;
import com.l2jmobius.gameserver.instancemanager.DuelManager;
import com.l2jmobius.gameserver.instancemanager.FishingChampionshipManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
import com.l2jmobius.gameserver.instancemanager.FortSiegeManager;
import com.l2jmobius.gameserver.instancemanager.FourSepulchersManager;
import com.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
import com.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jmobius.gameserver.instancemanager.MercTicketManager;
import com.l2jmobius.gameserver.instancemanager.PetitionManager;
import com.l2jmobius.gameserver.instancemanager.QuestManager;
import com.l2jmobius.gameserver.instancemanager.RaidBossPointsManager;
import com.l2jmobius.gameserver.instancemanager.RaidBossSpawnManager;
import com.l2jmobius.gameserver.instancemanager.SiegeManager;
import com.l2jmobius.gameserver.model.L2Manor;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.PartyMatchRoomList;
import com.l2jmobius.gameserver.model.PartyMatchWaitingList;
import com.l2jmobius.gameserver.model.entity.Announcements;
import com.l2jmobius.gameserver.model.entity.Hero;
import com.l2jmobius.gameserver.model.entity.MonsterRace;
import com.l2jmobius.gameserver.model.entity.event.manager.EventManager;
import com.l2jmobius.gameserver.model.entity.olympiad.Olympiad;
import com.l2jmobius.gameserver.model.entity.sevensigns.SevenSigns;
import com.l2jmobius.gameserver.model.entity.sevensigns.SevenSignsFestival;
import com.l2jmobius.gameserver.model.entity.siege.clanhalls.BanditStrongholdSiege;
import com.l2jmobius.gameserver.model.entity.siege.clanhalls.DevastatedCastle;
import com.l2jmobius.gameserver.model.entity.siege.clanhalls.FortressOfResistance;
import com.l2jmobius.gameserver.model.multisell.L2Multisell;
import com.l2jmobius.gameserver.model.spawn.AutoSpawn;
import com.l2jmobius.gameserver.network.L2GameClient;
import com.l2jmobius.gameserver.network.L2GamePacketHandler;
import com.l2jmobius.gameserver.script.EventDroplist;
import com.l2jmobius.gameserver.script.faenor.FaenorScriptEngine;
import com.l2jmobius.gameserver.scripting.CompiledScriptCache;
import com.l2jmobius.gameserver.scripting.L2ScriptEngineManager;
import com.l2jmobius.gameserver.taskmanager.TaskManager;
import com.l2jmobius.gameserver.thread.LoginServerThread;
import com.l2jmobius.gameserver.thread.daemons.DeadlockDetector;
import com.l2jmobius.gameserver.thread.daemons.ItemsAutoDestroy;
import com.l2jmobius.gameserver.thread.daemons.PcPoint;
import com.l2jmobius.gameserver.util.DynamicExtension;
import com.l2jmobius.status.Status;
public class GameServer
{
private static Logger LOGGER = Logger.getLogger(GameServer.class.getName());
// Local Constants
private static final String LOG_FOLDER = "log"; // Name of folder for log file
private static final String LOG_NAME = "./config/others/log.cfg"; // Name of log file
private static SelectorThread<L2GameClient> _selectorThread;
private static LoginServerThread _loginThread;
private static L2GamePacketHandler _gamePacketHandler;
private static Status _statusServer;
public static final Calendar dateTimeServerStarted = Calendar.getInstance();
public static void main(String[] args) throws Exception
{
Server.serverMode = Server.MODE_GAMESERVER;
// Create log folder
final File logFolder = new File(Config.DATAPACK_ROOT, LOG_FOLDER);
logFolder.mkdir();
// Create input stream for log file -- or store file data into memory
try (InputStream is = new FileInputStream(new File(LOG_NAME)))
{
LogManager.getLogManager().readConfiguration(is);
}
final long serverLoadStart = System.currentTimeMillis();
// Load GameServer Configs
Config.load();
Util.printSection("Database");
DatabaseFactory.getInstance();
LOGGER.info("L2DatabaseFactory: loaded.");
Util.printSection("Threads");
ThreadPoolManager.init();
if (Config.DEADLOCKCHECK_INTIAL_TIME > 0)
{
ThreadPoolManager.scheduleAtFixedRate(DeadlockDetector.getInstance(), Config.DEADLOCKCHECK_INTIAL_TIME, Config.DEADLOCKCHECK_DELAY_TIME);
}
new File(Config.DATAPACK_ROOT, "data/clans").mkdirs();
new File(Config.DATAPACK_ROOT, "data/crests").mkdirs();
new File(Config.DATAPACK_ROOT, "data/pathnode").mkdirs();
new File(Config.DATAPACK_ROOT, "data/geodata").mkdirs();
HtmCache.getInstance();
CrestCache.getInstance();
L2ScriptEngineManager.getInstance();
nProtect.getInstance();
if (nProtect.isEnabled())
{
LOGGER.info("nProtect System Enabled");
}
Util.printSection("World");
L2World.getInstance();
MapRegionTable.getInstance();
Announcements.getInstance();
AutoAnnouncementHandler.getInstance();
if (!IdFactory.getInstance().isInitialized())
{
LOGGER.info("Could not read object IDs from DB. Please Check Your Data.");
throw new Exception("Could not initialize the ID factory");
}
GlobalVariablesManager.getInstance();
StaticObjects.getInstance();
TeleportLocationTable.getInstance();
PartyMatchWaitingList.getInstance();
PartyMatchRoomList.getInstance();
GameTimeController.getInstance();
CharNameTable.getInstance();
ExperienceData.getInstance();
DuelManager.getInstance();
if (Config.ENABLE_CLASS_DAMAGES)
{
ClassDamageManager.loadConfig();
}
if (Config.AUTOSAVE_DELAY_TIME > 0)
{
AutoSaveManager.getInstance().startAutoSaveManager();
}
Util.printSection("Skills");
if (!SkillTable.getInstance().isInitialized())
{
LOGGER.info("Could not find the extraced files. Please Check Your Data.");
throw new Exception("Could not initialize the skill table");
}
SkillTreeTable.getInstance();
SkillSpellbookTable.getInstance();
NobleSkillTable.getInstance();
HeroSkillTable.getInstance();
LOGGER.info("Skills: All skills loaded.");
Util.printSection("Items");
ItemTable.getInstance();
ArmorSetsTable.getInstance();
if (Config.CUSTOM_ARMORSETS_TABLE)
{
CustomArmorSetsTable.getInstance();
}
ExtractableItemsData.getInstance();
SummonItemsData.getInstance();
if (Config.ALLOWFISHING)
{
FishTable.getInstance();
}
Util.printSection("Npc");
BufferTable.getInstance();
NpcWalkerRoutesTable.getInstance().load();
if (!NpcTable.getInstance().isInitialized())
{
LOGGER.info("Could not find the extracted files. Please Check Your Data.");
throw new Exception("Could not initialize the npc table");
}
Util.printSection("Characters");
if (Config.ENABLE_COMMUNITY_BOARD)
{
ForumsBBSManager.getInstance().initRoot();
}
ClanTable.getInstance();
CharTemplateTable.getInstance();
LevelUpData.getInstance();
if (!HennaTable.getInstance().isInitialized())
{
throw new Exception("Could not initialize the Henna Table");
}
if (!HennaTreeTable.getInstance().isInitialized())
{
throw new Exception("Could not initialize the Henna Tree Table");
}
if (!HelperBuffTable.getInstance().isInitialized())
{
throw new Exception("Could not initialize the Helper Buff Table");
}
Util.printSection("Geodata");
GeoData.getInstance();
if (Config.PATHFINDING > 0)
{
PathFinding.getInstance();
}
Util.printSection("Economy");
TradeController.getInstance();
L2Multisell.getInstance();
LOGGER.info("Multisell: loaded.");
Util.printSection("Clan Halls");
ClanHallManager.getInstance();
FortressOfResistance.getInstance();
DevastatedCastle.getInstance();
BanditStrongholdSiege.getInstance();
AuctionManager.getInstance();
Util.printSection("Zone");
ZoneData.getInstance();
Util.printSection("Spawnlist");
if (!Config.ALT_DEV_NO_SPAWNS)
{
SpawnTable.getInstance();
}
else
{
LOGGER.info("Spawn: disable load.");
}
if (!Config.ALT_DEV_NO_RB)
{
RaidBossSpawnManager.getInstance();
GrandBossManager.getInstance();
RaidBossPointsManager.init();
}
else
{
LOGGER.info("RaidBoss: disable load.");
}
DayNightSpawnManager.getInstance().notifyChangeMode();
Util.printSection("Dimensional Rift");
DimensionalRiftManager.getInstance();
Util.printSection("Misc");
RecipeTable.getInstance();
RecipeController.getInstance();
EventDroplist.getInstance();
AugmentationData.getInstance();
MonsterRace.getInstance();
MercTicketManager.getInstance();
PetitionManager.getInstance();
CursedWeaponsManager.getInstance();
TaskManager.getInstance();
L2PetDataTable.getInstance().loadPetsData();
if (Config.ACCEPT_GEOEDITOR_CONN)
{
GeoEditorListener.getInstance();
}
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance();
}
if ((Config.AUTODESTROY_ITEM_AFTER > 0) || (Config.HERB_AUTO_DESTROY_TIME > 0))
{
ItemsAutoDestroy.getInstance();
}
Util.printSection("Manor");
L2Manor.getInstance();
CastleManorManager.getInstance();
Util.printSection("Castles");
CastleManager.getInstance();
SiegeManager.getInstance();
FortManager.getInstance();
FortSiegeManager.getInstance();
CrownManager.getInstance();
Util.printSection("Boat");
BoatManager.getInstance();
Util.printSection("Doors");
DoorTable.getInstance().parseData();
Util.printSection("Four Sepulchers");
FourSepulchersManager.getInstance();
Util.printSection("Seven Signs");
SevenSigns.getInstance();
SevenSignsFestival.getInstance();
AutoSpawn.getInstance();
AutoChatHandler.getInstance();
Util.printSection("Olympiad System");
Olympiad.getInstance();
Hero.getInstance();
Util.printSection("Access Levels");
AccessLevels.getInstance();
AdminCommandAccessRights.getInstance();
GmListTable.getInstance();
Util.printSection("Handlers");
ItemHandler.getInstance();
SkillHandler.getInstance();
AdminCommandHandler.getInstance();
UserCommandHandler.getInstance();
VoicedCommandHandler.getInstance();
LOGGER.info("AutoChatHandler : Loaded " + AutoChatHandler.getInstance().size() + " handlers in total.");
LOGGER.info("AutoSpawnHandler : Loaded " + AutoSpawn.getInstance().size() + " handlers in total.");
Runtime.getRuntime().addShutdownHook(Shutdown.getInstance());
try
{
DoorTable doorTable = DoorTable.getInstance();
// Opened by players like L2OFF
// doorTable.getDoor(19160010).openMe();
// doorTable.getDoor(19160011).openMe();
doorTable.getDoor(19160012).openMe();
doorTable.getDoor(19160013).openMe();
doorTable.getDoor(19160014).openMe();
doorTable.getDoor(19160015).openMe();
doorTable.getDoor(19160016).openMe();
doorTable.getDoor(19160017).openMe();
doorTable.getDoor(24190001).openMe();
doorTable.getDoor(24190002).openMe();
doorTable.getDoor(24190003).openMe();
doorTable.getDoor(24190004).openMe();
doorTable.getDoor(23180001).openMe();
doorTable.getDoor(23180002).openMe();
doorTable.getDoor(23180003).openMe();
doorTable.getDoor(23180004).openMe();
doorTable.getDoor(23180005).openMe();
doorTable.getDoor(23180006).openMe();
doorTable.checkAutoOpen();
}
catch (NullPointerException e)
{
LOGGER.info("There is errors in your Door.csv file. Update door.csv");
}
Util.printSection("Scripts");
if (!Config.ALT_DEV_NO_SCRIPT)
{
final File scripts = new File(Config.DATAPACK_ROOT, "data/scripts.cfg");
L2ScriptEngineManager.getInstance().executeScriptsList(scripts);
final CompiledScriptCache compiledScriptCache = L2ScriptEngineManager.getInstance().getCompiledScriptCache();
if (compiledScriptCache == null)
{
LOGGER.info("Compiled Scripts Cache is disabled.");
}
else
{
compiledScriptCache.purge();
if (compiledScriptCache.isModified())
{
compiledScriptCache.save();
LOGGER.info("Compiled Scripts Cache was saved.");
}
else
{
LOGGER.info("Compiled Scripts Cache is up-to-date.");
}
}
FaenorScriptEngine.getInstance();
}
else
{
LOGGER.info("Script: disable load.");
}
if (Config.ALT_FISH_CHAMPIONSHIP_ENABLED)
{
FishingChampionshipManager.getInstance();
}
/* QUESTS */
Util.printSection("Quests");
if (!Config.ALT_DEV_NO_QUESTS)
{
if (QuestManager.getInstance().getQuests().size() == 0)
{
QuestManager.getInstance().reloadAllQuests();
}
else
{
QuestManager.getInstance().report();
}
}
else
{
QuestManager.getInstance().unloadAllQuests();
}
Util.printSection("Game Server");
LOGGER.info("IdFactory: Free ObjectID's remaining: " + IdFactory.getInstance().size());
try
{
DynamicExtension.getInstance();
}
catch (Exception ex)
{
LOGGER.info("DynamicExtension could not be loaded and initialized" + ex);
}
Util.printSection("Custom Mods");
if (Config.L2JMOD_ALLOW_WEDDING || Config.ALLOW_AWAY_STATUS || Config.PCB_ENABLE)
{
if (Config.L2JMOD_ALLOW_WEDDING)
{
CoupleManager.getInstance();
}
if (Config.ALLOW_AWAY_STATUS)
{
AwayManager.getInstance();
}
if (Config.PCB_ENABLE)
{
ThreadPoolManager.scheduleAtFixedRate(PcPoint.getInstance(), Config.PCB_INTERVAL * 1000, Config.PCB_INTERVAL * 1000);
}
}
else
{
LOGGER.info("All custom mods are Disabled.");
}
Util.printSection("EventManager");
EventManager.getInstance().startEventRegistration();
if (EventManager.TVT_EVENT_ENABLED || EventManager.CTF_EVENT_ENABLED || EventManager.DM_EVENT_ENABLED)
{
if (EventManager.TVT_EVENT_ENABLED)
{
LOGGER.info("TVT Event is Enabled.");
}
if (EventManager.CTF_EVENT_ENABLED)
{
LOGGER.info("CTF Event is Enabled.");
}
if (EventManager.DM_EVENT_ENABLED)
{
LOGGER.info("DM Event is Enabled.");
}
}
else
{
LOGGER.info("All events are Disabled.");
}
if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
{
OfflineTradeTable.restoreOfflineTraders();
}
Util.printSection("Protection");
if (Config.CHECK_SKILLS_ON_ENTER)
{
LOGGER.info("Check skills on enter actived.");
}
if (Config.CHECK_NAME_ON_LOGIN)
{
LOGGER.info("Check bad name on enter actived.");
}
if (Config.PROTECTED_ENCHANT)
{
LOGGER.info("Check OverEnchant items on enter actived.");
}
if (Config.BYPASS_VALIDATION)
{
LOGGER.info("Bypass Validation actived.");
}
if (Config.L2WALKER_PROTEC)
{
LOGGER.info("L2Walker protection actived.");
}
Util.printSection("Info");
LOGGER.info("Maximum Numbers of Connected Players: " + Config.MAXIMUM_ONLINE_USERS);
LOGGER.info("GameServer Started, free memory " + Memory.getFreeMemory() + " Mb of " + Memory.getTotalMemory() + " Mb");
LOGGER.info("Used memory: " + Memory.getUsedMemory() + " MB");
Util.printSection("Status");
System.gc();
LOGGER.info("Server Loaded in " + ((System.currentTimeMillis() - serverLoadStart) / 1000) + " seconds");
ServerStatus.getInstance();
// Load telnet status
Util.printSection("Telnet");
if (Config.IS_TELNET_ENABLED)
{
_statusServer = new Status(Server.serverMode);
_statusServer.start();
}
else
{
LOGGER.info("Telnet server is disabled.");
}
Util.printSection("Login");
_loginThread = LoginServerThread.getInstance();
_loginThread.start();
final SelectorConfig sc = new SelectorConfig();
sc.setMaxReadPerPass(NetcoreConfig.getInstance().MMO_MAX_READ_PER_PASS);
sc.setMaxSendPerPass(NetcoreConfig.getInstance().MMO_MAX_SEND_PER_PASS);
sc.setSleepTime(NetcoreConfig.getInstance().MMO_SELECTOR_SLEEP_TIME);
sc.setHelperBufferCount(NetcoreConfig.getInstance().MMO_HELPER_BUFFER_COUNT);
_gamePacketHandler = new L2GamePacketHandler();
_selectorThread = new SelectorThread<>(sc, _gamePacketHandler, _gamePacketHandler, _gamePacketHandler, new IPv4Filter());
InetAddress bindAddress = null;
if (!Config.GAMESERVER_HOSTNAME.equals("*"))
{
try
{
bindAddress = InetAddress.getByName(Config.GAMESERVER_HOSTNAME);
}
catch (UnknownHostException e1)
{
LOGGER.warning("The GameServer bind address is invalid, using all avaliable IPs. Reason: " + e1);
}
}
try
{
_selectorThread.openServerSocket(bindAddress, Config.PORT_GAME);
}
catch (IOException e)
{
LOGGER.severe("Failed to open server socket. Reason: " + e);
System.exit(1);
}
_selectorThread.start();
}
public static SelectorThread<L2GameClient> getSelectorThread()
{
return _selectorThread;
}
}

View File

@ -0,0 +1,283 @@
/*
* 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.gameserver;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Logger;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.gameserver.ai.CtrlEvent;
import com.l2jmobius.gameserver.instancemanager.DayNightSpawnManager;
import com.l2jmobius.gameserver.model.actor.L2Character;
/**
* This class ...
* @version $Revision: 1.1.4.8 $ $Date: 2005/04/06 16:13:24 $
*/
public class GameTimeController
{
static final Logger LOGGER = Logger.getLogger(GameTimeController.class.getName());
public static final int TICKS_PER_SECOND = 10;
public static final int MILLIS_IN_TICK = 1000 / TICKS_PER_SECOND;
private static GameTimeController _instance = new GameTimeController();
protected static int _gameTicks;
protected static long _gameStartTime;
protected static boolean _isNight = false;
private static List<L2Character> _movingObjects = new ArrayList<>();
protected static TimerThread _timer;
private final ScheduledFuture<?> _timerWatcher;
/**
* one ingame day is 240 real minutes
* @return
*/
public static GameTimeController getInstance()
{
return _instance;
}
private GameTimeController()
{
_gameStartTime = System.currentTimeMillis() - 3600000; // offset so that the server starts a day begin
_gameTicks = 3600000 / MILLIS_IN_TICK; // offset so that the server starts a day begin
_timer = new TimerThread();
_timer.start();
_timerWatcher = ThreadPoolManager.scheduleAtFixedRate(new TimerWatcher(), 0, 1000);
ThreadPoolManager.scheduleAtFixedRate(new BroadcastSunState(), 0, 600000);
}
public boolean isNowNight()
{
return _isNight;
}
public int getGameTime()
{
return _gameTicks / (TICKS_PER_SECOND * 10);
}
public static int getGameTicks()
{
return _gameTicks;
}
/**
* Add a L2Character to movingObjects of GameTimeController.<BR>
* <BR>
* <B><U> Concept</U> :</B><BR>
* <BR>
* All L2Character in movement are identified in <B>movingObjects</B> of GameTimeController.<BR>
* <BR>
* @param cha The L2Character to add to movingObjects of GameTimeController
*/
public synchronized void registerMovingObject(L2Character cha)
{
if (cha == null)
{
return;
}
if (!_movingObjects.contains(cha))
{
_movingObjects.add(cha);
}
}
/**
* Move all L2Characters contained in movingObjects of GameTimeController.<BR>
* <BR>
* <B><U> Concept</U> :</B><BR>
* <BR>
* All L2Character in movement are identified in <B>movingObjects</B> of GameTimeController.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Update the position of each L2Character</li>
* <li>If movement is finished, the L2Character is removed from movingObjects</li>
* <li>Create a task to update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object then notify AI with EVT_ARRIVED</li><BR>
* <BR>
*/
protected synchronized void moveObjects()
{
// Get all L2Character from the ArrayList movingObjects and put them into a table
final L2Character[] chars = _movingObjects.toArray(new L2Character[_movingObjects.size()]);
// Create an ArrayList to contain all L2Character that are arrived to destination
List<L2Character> ended = null;
// Go throw the table containing L2Character in movement
for (L2Character cha : chars)
{
// Update the position of the L2Character and return True if the movement is finished
final boolean end = cha.updatePosition(_gameTicks);
// If movement is finished, the L2Character is removed from movingObjects and added to the ArrayList ended
if (end)
{
_movingObjects.remove(cha);
if (ended == null)
{
ended = new ArrayList<>();
}
ended.add(cha);
}
}
// Create a task to update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object
// then notify AI with EVT_ARRIVED
// TODO: maybe a general TP is needed for that kinda stuff (all knownlist updates should be done in a TP anyway).
if (ended != null)
{
ThreadPoolManager.execute(new MovingObjectArrived(ended));
}
}
public void stopTimer()
{
_timerWatcher.cancel(true);
_timer.interrupt();
}
class TimerThread extends Thread
{
protected Exception _error;
public TimerThread()
{
super("GameTimeController");
setDaemon(true);
setPriority(MAX_PRIORITY);
}
@Override
public void run()
{
for (;;)
{
final int _oldTicks = _gameTicks; // save old ticks value to avoid moving objects 2x in same tick
long runtime = System.currentTimeMillis() - _gameStartTime; // from server boot to now
_gameTicks = (int) (runtime / MILLIS_IN_TICK); // new ticks value (ticks now)
if (_oldTicks != _gameTicks)
{
moveObjects(); // XXX: if this makes objects go slower, remove it
// but I think it can't make that effect. is it better to call moveObjects() twice in same
// tick to make-up for missed tick ? or is it better to ignore missed tick ?
// (will happen very rarely but it will happen ... on garbage collection definitely)
}
runtime = System.currentTimeMillis() - _gameStartTime - runtime;
// calculate sleep time... time needed to next tick minus time it takes to call moveObjects()
final int sleepTime = (1 + MILLIS_IN_TICK) - ((int) runtime % MILLIS_IN_TICK);
// LOGGER.finest("TICK: "+_gameTicks);
try
{
sleep(sleepTime); // hope other threads will have much more cpu time available now
}
catch (InterruptedException ie)
{
// nothing
}
// SelectorThread most of all
}
}
}
class TimerWatcher implements Runnable
{
@Override
public void run()
{
if (!_timer.isAlive())
{
final String time = new SimpleDateFormat("HH:mm:ss").format(new Date());
LOGGER.warning(time + " TimerThread stop with following error. restart it.");
if (_timer._error != null)
{
_timer._error.printStackTrace();
}
_timer = new TimerThread();
_timer.start();
}
}
}
/**
* Update the _knownObject and _knowPlayers of each L2Character that finished its movement and of their already known L2Object then notify AI with EVT_ARRIVED.<BR>
* <BR>
*/
class MovingObjectArrived implements Runnable
{
private final List<L2Character> _ended;
MovingObjectArrived(List<L2Character> ended)
{
_ended = ended;
}
@Override
public void run()
{
for (L2Character cha : _ended)
{
try
{
cha.getKnownList().updateKnownObjects();
cha.getAI().notifyEvent(CtrlEvent.EVT_ARRIVED);
}
catch (NullPointerException e)
{
}
}
}
}
class BroadcastSunState implements Runnable
{
@Override
public void run()
{
final int h = (getGameTime() / 60) % 24; // Time in hour
final boolean tempIsNight = h < 6;
// If diff day/night state
if (tempIsNight != _isNight)
{
// Set current day/night varible to value of temp varible
_isNight = tempIsNight;
DayNightSpawnManager.getInstance().notifyChangeMode();
}
}
}
}

View File

@ -0,0 +1,733 @@
/*
* 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.gameserver;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.datatables.csv.RecipeTable;
import com.l2jmobius.gameserver.model.Inventory;
import com.l2jmobius.gameserver.model.L2ManufactureItem;
import com.l2jmobius.gameserver.model.L2RecipeList;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.model.actor.instance.L2ItemInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2RecipeInstance;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import com.l2jmobius.gameserver.network.serverpackets.ItemList;
import com.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;
import com.l2jmobius.gameserver.network.serverpackets.RecipeBookItemList;
import com.l2jmobius.gameserver.network.serverpackets.RecipeItemMakeInfo;
import com.l2jmobius.gameserver.network.serverpackets.RecipeShopItemInfo;
import com.l2jmobius.gameserver.network.serverpackets.SetupGauge;
import com.l2jmobius.gameserver.network.serverpackets.StatusUpdate;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import com.l2jmobius.gameserver.skills.Stats;
import com.l2jmobius.gameserver.util.Util;
public class RecipeController
{
protected static final Logger LOGGER = Logger.getLogger(RecipeController.class.getName());
private static RecipeController _instance;
protected static final Map<L2PcInstance, RecipeItemMaker> _activeMakers = Collections.synchronizedMap(new WeakHashMap<L2PcInstance, RecipeItemMaker>());
public static RecipeController getInstance()
{
return _instance == null ? _instance = new RecipeController() : _instance;
}
public synchronized void requestBookOpen(L2PcInstance player, boolean isDwarvenCraft)
{
RecipeItemMaker maker = null;
if (Config.ALT_GAME_CREATION)
{
maker = _activeMakers.get(player);
}
if (maker == null)
{
RecipeBookItemList response = new RecipeBookItemList(isDwarvenCraft, player.getMaxMp());
response.addRecipes(isDwarvenCraft ? player.getDwarvenRecipeBook() : player.getCommonRecipeBook());
player.sendPacket(response);
return;
}
SystemMessage sm = new SystemMessage(SystemMessageId.CANT_ALTER_RECIPEBOOK_WHILE_CRAFTING);
player.sendPacket(sm);
return;
}
public synchronized void requestMakeItemAbort(L2PcInstance player)
{
_activeMakers.remove(player); // TODO: anything else here?
}
public synchronized void requestManufactureItem(L2PcInstance manufacturer, int recipeListId, L2PcInstance player)
{
L2RecipeList recipeList = getValidRecipeList(player, recipeListId);
if (recipeList == null)
{
return;
}
List<L2RecipeList> dwarfRecipes = Arrays.asList(manufacturer.getDwarvenRecipeBook());
List<L2RecipeList> commonRecipes = Arrays.asList(manufacturer.getCommonRecipeBook());
if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList))
{
Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH);
return;
}
RecipeItemMaker maker;
if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(manufacturer)) != null)) // check if busy
{
player.sendMessage("Manufacturer is busy, please try later.");
return;
}
maker = new RecipeItemMaker(manufacturer, recipeList, player);
if (maker._isValid)
{
if (Config.ALT_GAME_CREATION)
{
_activeMakers.put(manufacturer, maker);
ThreadPoolManager.schedule(maker, 100);
}
else
{
maker.run();
}
}
}
public synchronized void requestMakeItem(L2PcInstance player, int recipeListId)
{
if (player.isInDuel())
{
player.sendPacket(SystemMessageId.CANT_CRAFT_DURING_COMBAT);
return;
}
L2RecipeList recipeList = getValidRecipeList(player, recipeListId);
if (recipeList == null)
{
return;
}
List<L2RecipeList> dwarfRecipes = Arrays.asList(player.getDwarvenRecipeBook());
List<L2RecipeList> commonRecipes = Arrays.asList(player.getCommonRecipeBook());
if (!dwarfRecipes.contains(recipeList) && !commonRecipes.contains(recipeList))
{
Util.handleIllegalPlayerAction(player, "Warning!! Character " + player.getName() + " of account " + player.getAccountName() + " sent a false recipe id.", Config.DEFAULT_PUNISH);
return;
}
RecipeItemMaker maker;
// check if already busy (possible in alt mode only)
if (Config.ALT_GAME_CREATION && ((maker = _activeMakers.get(player)) != null))
{
final SystemMessage sm = new SystemMessage(SystemMessageId.S1_S2);
sm.addString("You are busy creating ");
sm.addItemName(recipeList.getItemId());
player.sendPacket(sm);
return;
}
maker = new RecipeItemMaker(player, recipeList, player);
if (maker._isValid)
{
if (Config.ALT_GAME_CREATION)
{
_activeMakers.put(player, maker);
ThreadPoolManager.schedule(maker, 100);
}
else
{
maker.run();
}
}
}
private class RecipeItemMaker implements Runnable
{
protected boolean _isValid;
protected List<TempItem> _items = null;
protected final L2RecipeList _recipeList;
protected final L2PcInstance _player; // "crafter"
protected final L2PcInstance _target; // "customer"
protected final L2Skill _skill;
protected final int _skillId;
protected final int _skillLevel;
protected double _creationPasses;
protected double _manaRequired;
protected int _price;
protected int _totalItems;
protected int _delay;
public RecipeItemMaker(L2PcInstance pPlayer, L2RecipeList pRecipeList, L2PcInstance pTarget)
{
_player = pPlayer;
_target = pTarget;
_recipeList = pRecipeList;
_isValid = false;
_skillId = _recipeList.isDwarvenRecipe() ? L2Skill.SKILL_CREATE_DWARVEN : L2Skill.SKILL_CREATE_COMMON;
_skillLevel = _player.getSkillLevel(_skillId);
_skill = _player.getKnownSkill(_skillId);
_player.isInCraftMode(true);
if (_player.isAlikeDead())
{
_player.sendMessage("Dead people don't craft.");
_player.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
if (_target.isAlikeDead())
{
_target.sendMessage("Dead customers can't use manufacture.");
_target.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
if (_target.isProcessingTransaction())
{
_target.sendMessage("You are busy.");
_target.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
if (_player.isProcessingTransaction())
{
if (_player != _target)
{
_target.sendMessage("Manufacturer " + _player.getName() + " is busy.");
}
_player.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
// validate recipe list
if ((_recipeList == null) || (_recipeList.getRecipes().length == 0))
{
_player.sendMessage("No such recipe");
_player.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
_manaRequired = _recipeList.getMpCost();
// validate skill level
if (_recipeList.getLevel() > _skillLevel)
{
_player.sendMessage("Need skill level " + _recipeList.getLevel());
_player.sendPacket(ActionFailed.STATIC_PACKET);
abort();
return;
}
// check that customer can afford to pay for creation services
if (_player != _target)
{
for (L2ManufactureItem temp : _player.getCreateList().getList())
{
if (temp.getRecipeId() == _recipeList.getId()) // find recipe for item we want manufactured
{
_price = temp.getCost();
if (_target.getAdena() < _price) // check price
{
_target.sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
abort();
return;
}
break;
}
}
}
_items = listItems(false);
// make temporary items
if (_items == null)
{
abort();
return;
}
// calculate reference price
for (TempItem i : _items)
{
_totalItems += i.getQuantity();
}
// initial mana check requires MP as written on recipe
if (_player.getCurrentMp() < _manaRequired)
{
_target.sendPacket(SystemMessageId.NOT_ENOUGH_MP);
abort();
return;
}
// determine number of creation passes needed
// can "equip" skillLevel items each pass
_creationPasses = (_totalItems / _skillLevel) + ((_totalItems % _skillLevel) != 0 ? 1 : 0);
if (Config.ALT_GAME_CREATION && (_creationPasses != 0))
{
_manaRequired /= _creationPasses; // checks to validateMp() will only need portion of mp for one pass
}
updateMakeInfo(true);
updateCurMp();
updateCurLoad();
_player.isInCraftMode(false);
_isValid = true;
}
@Override
public void run()
{
if (!Config.IS_CRAFTING_ENABLED)
{
_target.sendMessage("Item creation is currently disabled.");
abort();
return;
}
if ((_player == null) || (_target == null))
{
LOGGER.warning("player or target == null (disconnected?), aborting" + _target + _player);
abort();
return;
}
if ((_player.isOnline() == 0) || (_target.isOnline() == 0))
{
LOGGER.warning("player or target is not online, aborting " + _target + _player);
abort();
return;
}
if (Config.ALT_GAME_CREATION && (_activeMakers.get(_player) == null))
{
if (_target != _player)
{
_target.sendMessage("Manufacture aborted");
_player.sendMessage("Manufacture aborted");
}
else
{
_player.sendMessage("Item creation aborted");
}
abort();
return;
}
if (Config.ALT_GAME_CREATION && !_items.isEmpty())
{
// check mana
if (!validateMp())
{
return;
}
// use some mp
_player.reduceCurrentMp(_manaRequired);
// update craft window mp bar
updateCurMp();
// grab (equip) some more items with a nice msg to player
grabSomeItems();
// if still not empty, schedule another pass
if (!_items.isEmpty())
{
// divided by RATE_CONSUMABLES_COST to remove craft time increase on higher consumables rates
_delay = (int) ((Config.ALT_GAME_CREATION_SPEED * _player.getMReuseRate(_skill) * GameTimeController.TICKS_PER_SECOND) / Config.RATE_CONSUMABLE_COST) * GameTimeController.MILLIS_IN_TICK;
// FIXME: please fix this packet to show crafting animation (somebody)
MagicSkillUse msk = new MagicSkillUse(_player, _skillId, _skillLevel, _delay, 0);
_player.broadcastPacket(msk);
_player.sendPacket(new SetupGauge(0, _delay));
ThreadPoolManager.schedule(this, 100 + _delay);
}
else
{
// for alt mode, sleep delay msec before finishing
_player.sendPacket(new SetupGauge(0, _delay));
try
{
Thread.sleep(_delay);
}
catch (InterruptedException e)
{
}
finally
{
finishCrafting();
}
}
}
// for old craft mode just finish
else
{
finishCrafting();
}
}
private void finishCrafting()
{
if (!Config.ALT_GAME_CREATION)
{
_player.reduceCurrentMp(_manaRequired);
}
// first take adena for manufacture
if ((_target != _player) && (_price > 0)) // customer must pay for services
{
// attempt to pay for item
L2ItemInstance adenatransfer = _target.transferItem("PayManufacture", _target.getInventory().getAdenaInstance().getObjectId(), _price, _player.getInventory(), _player);
if (adenatransfer == null)
{
_target.sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
abort();
return;
}
}
_items = listItems(true);
// this line actually takes materials from inventory
if (_items == null)
{
// handle possible cheaters here
// (they click craft then try to get rid of items in order to get free craft)
}
else if (Rnd.get(100) < _recipeList.getSuccessRate())
{
rewardPlayer(); // and immediately puts created item in its place
updateMakeInfo(true);
}
else
{
_player.sendMessage("Item(s) failed to create");
if (_target != _player)
{
_target.sendMessage("Item(s) failed to create");
}
updateMakeInfo(false);
}
// update load and mana bar of craft window
updateCurMp();
updateCurLoad();
_activeMakers.remove(_player);
_player.isInCraftMode(false);
_target.sendPacket(new ItemList(_target, false));
}
private void updateMakeInfo(boolean success)
{
if (_target == _player)
{
_target.sendPacket(new RecipeItemMakeInfo(_recipeList.getId(), _target, success));
}
else
{
_target.sendPacket(new RecipeShopItemInfo(_player.getObjectId(), _recipeList.getId()));
}
}
private void updateCurLoad()
{
StatusUpdate su = new StatusUpdate(_target.getObjectId());
su.addAttribute(StatusUpdate.CUR_LOAD, _target.getCurrentLoad());
_target.sendPacket(su);
}
private void updateCurMp()
{
StatusUpdate su = new StatusUpdate(_target.getObjectId());
su.addAttribute(StatusUpdate.CUR_MP, (int) _target.getCurrentMp());
_target.sendPacket(su);
}
private void grabSomeItems()
{
int numItems = _skillLevel;
while ((numItems > 0) && !_items.isEmpty())
{
TempItem item = _items.get(0);
int count = item.getQuantity();
if (count >= numItems)
{
count = numItems;
}
item.setQuantity(item.getQuantity() - count);
if (item.getQuantity() <= 0)
{
_items.remove(0);
}
else
{
_items.set(0, item);
}
numItems -= count;
if (_target == _player)
{
// you equipped ...
SystemMessage sm = new SystemMessage(SystemMessageId.S1_S2_EQUIPPED);
sm.addNumber(count);
sm.addItemName(item.getItemId());
_player.sendPacket(sm);
}
else
{
_target.sendMessage("Manufacturer " + _player.getName() + " used " + count + " " + item.getItemName());
}
}
}
private boolean validateMp()
{
if (_player.getCurrentMp() < _manaRequired)
{
// rest (wait for MP)
if (Config.ALT_GAME_CREATION)
{
_player.sendPacket(new SetupGauge(0, _delay));
ThreadPoolManager.schedule(this, 100 + _delay);
}
// no rest - report no mana
else
{
_target.sendPacket(SystemMessageId.NOT_ENOUGH_MP);
abort();
}
return false;
}
return true;
}
private List<TempItem> listItems(boolean remove)
{
L2RecipeInstance[] recipes = _recipeList.getRecipes();
Inventory inv = _target.getInventory();
final List<TempItem> materials = new ArrayList<>();
for (L2RecipeInstance recipe : recipes)
{
final int quantity = _recipeList.isConsumable() ? (int) (recipe.getQuantity() * Config.RATE_CONSUMABLE_COST) : recipe.getQuantity();
if (quantity > 0)
{
final L2ItemInstance item = inv.getItemByItemId(recipe.getItemId());
// check materials
if ((item == null) || (item.getCount() < quantity))
{
_target.sendMessage("You dont have the right elements for making this item" + (_recipeList.isConsumable() && (Config.RATE_CONSUMABLE_COST != 1) ? ".\nDue to server rates you need " + Config.RATE_CONSUMABLE_COST + "x more material than listed in recipe" : ""));
abort();
return null;
}
// make new temporary object, just for counting puroses
TempItem temp = new TempItem(item, quantity);
materials.add(temp);
}
}
if (remove)
{
for (TempItem tmp : materials)
{
inv.destroyItemByItemId("Manufacture", tmp.getItemId(), tmp.getQuantity(), _target, _player);
}
}
return materials;
}
private void abort()
{
updateMakeInfo(false);
_player.isInCraftMode(false);
_activeMakers.remove(_player);
}
/**
* FIXME: This class should be in some other file, but I don't know where Class explanation: For item counting or checking purposes. When you don't want to modify inventory class contains itemId, quantity, ownerId, referencePrice, but not objectId
*/
private class TempItem
{
// no object id stored, this will be only "list" of items with it's owner
private final int _itemId;
private int _quantity;
private final String _itemName;
/**
* @param item
* @param quantity of that item
*/
public TempItem(L2ItemInstance item, int quantity)
{
super();
_itemId = item.getItemId();
_quantity = quantity;
_itemName = item.getItem().getName();
}
/**
* @return Returns the quantity.
*/
public int getQuantity()
{
return _quantity;
}
/**
* @param quantity The quantity to set.
*/
public void setQuantity(int quantity)
{
_quantity = quantity;
}
/**
* @return Returns the itemId.
*/
public int getItemId()
{
return _itemId;
}
/**
* @return Returns the itemName.
*/
public String getItemName()
{
return _itemName;
}
}
private void rewardPlayer()
{
final int itemId = _recipeList.getItemId();
final int itemCount = _recipeList.getCount();
final L2ItemInstance createdItem = _target.getInventory().addItem("Manufacture", itemId, itemCount, _target, _player);
// inform customer of earned item
SystemMessage sm = null;
if (itemCount > 1)
{
sm = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
sm.addItemName(itemId);
sm.addNumber(itemCount);
_target.sendPacket(sm);
}
else
{
sm = new SystemMessage(SystemMessageId.EARNED_ITEM);
sm.addItemName(itemId);
_target.sendPacket(sm);
}
if (_target != _player)
{
// inform manufacturer of earned profit
sm = new SystemMessage(SystemMessageId.EARNED_ADENA);
sm.addNumber(_price);
_player.sendPacket(sm);
}
if (Config.ALT_GAME_CREATION)
{
final int recipeLevel = _recipeList.getLevel();
int exp = createdItem.getReferencePrice() * itemCount;
// one variation
// exp -= materialsRefPrice;
// mat. ref. price is not accurate so other method is better
if (exp < 0)
{
exp = 0;
}
// another variation
exp /= recipeLevel;
for (int i = _skillLevel; i > recipeLevel; i--)
{
exp /= 4;
}
final int sp = exp / 10;
// Added multiplication of Creation speed with XP/SP gain
// slower crafting -> more XP, faster crafting -> less XP
// you can use ALT_GAME_CREATION_XP_RATE/SP to
// modify XP/SP gained (default = 1)
_player.addExpAndSp((int) _player.calcStat(Stats.EXPSP_RATE, exp * Config.ALT_GAME_CREATION_XP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null), (int) _player.calcStat(Stats.EXPSP_RATE, sp * Config.ALT_GAME_CREATION_SP_RATE * Config.ALT_GAME_CREATION_SPEED, null, null));
}
updateMakeInfo(true); // success
}
}
private L2RecipeList getValidRecipeList(L2PcInstance player, int id)
{
final L2RecipeList recipeList = RecipeTable.getInstance().getRecipeList(id - 1);
if ((recipeList == null) || (recipeList.getRecipes().length == 0))
{
player.sendMessage("No recipe for: " + id);
player.isInCraftMode(false);
return null;
}
return recipeList;
}
}

View File

@ -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.gameserver;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Logger;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.commons.util.Memory;
import com.l2jmobius.commons.util.Util;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* Server status
* @author Nefer
* @version 1.0
*/
public class ServerStatus
{
protected static final Logger LOGGER = Logger.getLogger(ServerStatus.class.getName());
protected ScheduledFuture<?> _scheduledTask;
protected ServerStatus()
{
_scheduledTask = ThreadPoolManager.scheduleAtFixedRate(new ServerStatusTask(), 1800000, 3600000);
}
protected class ServerStatusTask implements Runnable
{
protected final SimpleDateFormat fmt = new SimpleDateFormat("H:mm.");
@Override
public void run()
{
int ActivePlayers = 0;
int OfflinePlayers = 0;
for (L2PcInstance player : L2World.getInstance().getAllPlayers())
{
if (player.isInOfflineMode())
{
OfflinePlayers++;
}
else
{
ActivePlayers++;
}
}
Util.printSection("Server Status");
LOGGER.info("Server Time: " + fmt.format(new Date(System.currentTimeMillis())));
LOGGER.info("Active Players Online: " + ActivePlayers);
LOGGER.info("Offline Players Online: " + OfflinePlayers);
LOGGER.info("Threads: " + Thread.activeCount());
LOGGER.info("Free Memory: " + Memory.getFreeMemory() + " MB");
LOGGER.info("Used memory: " + Memory.getUsedMemory() + " MB");
Util.printSection("Server Status");
}
}
public static ServerStatus getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final ServerStatus _instance = new ServerStatus();
}
}

View File

@ -0,0 +1,741 @@
/*
* 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.gameserver;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.datatables.BufferTable;
import com.l2jmobius.gameserver.datatables.OfflineTradeTable;
import com.l2jmobius.gameserver.instancemanager.AutoSaveManager;
import com.l2jmobius.gameserver.instancemanager.CastleManorManager;
import com.l2jmobius.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jmobius.gameserver.instancemanager.FishingChampionshipManager;
import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
import com.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager;
import com.l2jmobius.gameserver.instancemanager.QuestManager;
import com.l2jmobius.gameserver.instancemanager.RaidBossSpawnManager;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.entity.Announcements;
import com.l2jmobius.gameserver.model.entity.olympiad.Olympiad;
import com.l2jmobius.gameserver.model.entity.sevensigns.SevenSigns;
import com.l2jmobius.gameserver.model.entity.sevensigns.SevenSignsFestival;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.gameserverpackets.ServerStatus;
import com.l2jmobius.gameserver.network.serverpackets.ServerClose;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
import com.l2jmobius.gameserver.thread.LoginServerThread;
/**
* This class provides the functions for shutting down and restarting the server It closes all open client connections and saves all data.
* @version $Revision: 1.2.4.6 $ $Date: 2009/05/12 19:45:09 $
*/
public class Shutdown extends Thread
{
public enum ShutdownModeType1
{
SIGTERM("Terminating"),
SHUTDOWN("Shutting down"),
RESTART("Restarting"),
ABORT("Aborting"),
TASK_SHUT("Shuting down"),
TASK_RES("Restarting"),
TELL_SHUT("Shuting down"),
TELL_RES("Restarting");
private final String _modeText;
ShutdownModeType1(String modeText)
{
_modeText = modeText;
}
public String getText()
{
return _modeText;
}
}
protected static final Logger LOGGER = Logger.getLogger(Shutdown.class.getName());
private static Shutdown _instance;
private static Shutdown _counterInstance = null;
private int _secondsShut;
private int _shutdownMode;
private boolean _shutdownStarted;
/** 0 */
public static final int SIGTERM = 0;
/** 1 */
public static final int GM_SHUTDOWN = 1;
/** 2 */
public static final int GM_RESTART = 2;
/** 3 */
public static final int ABORT = 3;
/** 4 */
public static final int TASK_SHUTDOWN = 4;
/** 5 */
public static final int TASK_RESTART = 5;
/** 6 */
public static final int TELL_SHUTDOWN = 6;
/** 7 */
public static final int TELL_RESTART = 7;
private static final String[] MODE_TEXT =
{
"SIGTERM",
"shutting down",
"restarting",
"aborting", // standart
"shutting down",
"restarting", // task
"shutting down",
"restarting"
}; // telnet
/**
* This function starts a shutdown count down from Telnet (Copied from Function startShutdown())
* @param IP Which Issued shutdown command
* @param seconds seconds until shutdown
* @param restart true if the server will restart after shutdown
*/
public void startTelnetShutdown(String IP, int seconds, boolean restart)
{
final Announcements _an = Announcements.getInstance();
LOGGER.warning("IP: " + IP + " issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
_an.announceToAll("Server is " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
if (restart)
{
_shutdownMode = TELL_RESTART;
}
else
{
_shutdownMode = TELL_SHUTDOWN;
}
if (_shutdownMode > 0)
{
_an.announceToAll("Server is " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
_an.announceToAll("Please exit game now!!");
}
if (_counterInstance != null)
{
_counterInstance._abort();
}
_counterInstance = new Shutdown(seconds, restart, false, true);
_counterInstance.start();
}
/**
* This function aborts a running countdown
* @param IP IP Which Issued shutdown command
*/
public void telnetAbort(String IP)
{
Announcements _an = Announcements.getInstance();
LOGGER.warning("IP: " + IP + " issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!");
_an.announceToAll("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!");
if (_counterInstance != null)
{
_counterInstance._abort();
}
}
/**
* Default constructor is only used internal to create the shutdown-hook instance
*/
public Shutdown()
{
_secondsShut = -1;
_shutdownMode = SIGTERM;
_shutdownStarted = false;
}
/**
* This creates a count down instance of Shutdown.
* @param seconds how many seconds until shutdown
* @param restart true is the server shall restart after shutdown
* @param task
* @param telnet
*/
public Shutdown(int seconds, boolean restart, boolean task, boolean telnet)
{
if (seconds < 0)
{
seconds = 0;
}
_secondsShut = seconds;
if (restart)
{
if (!task)
{
_shutdownMode = GM_RESTART;
}
else if (telnet)
{
_shutdownMode = TELL_RESTART;
}
else
{
_shutdownMode = TASK_RESTART;
}
}
else if (!task)
{
_shutdownMode = GM_SHUTDOWN;
}
else if (telnet)
{
_shutdownMode = TELL_SHUTDOWN;
}
else
{
_shutdownMode = TASK_SHUTDOWN;
}
_shutdownStarted = false;
}
/**
* get the shutdown-hook instance the shutdown-hook instance is created by the first call of this function, but it has to be registered externally.
* @return instance of Shutdown, to be used as shutdown hook
*/
public static Shutdown getInstance()
{
if (_instance == null)
{
_instance = new Shutdown();
}
return _instance;
}
public boolean isShutdownStarted()
{
boolean output = _shutdownStarted;
// if a counter is started, the value of shutdownstarted is of counterinstance
if (_counterInstance != null)
{
output = _counterInstance._shutdownStarted;
}
return output;
}
/**
* this function is called, when a new thread starts if this thread is the thread of getInstance, then this is the shutdown hook and we save all data and disconnect all clients. after this thread ends, the server will completely exit if this is not the thread of getInstance, then this is a
* countdown thread. we start the countdown, and when we finished it, and it was not aborted, we tell the shutdown-hook why we call exit, and then call exit when the exit status of the server is 1, startServer.sh / startServer.bat will restart the server.
*/
@Override
public void run()
{
/*
* // disallow new logins try { //Doesnt actually do anything //Server.gameServer.getLoginController().setMaxAllowedOnlinePlayers(0); } catch(Throwable t) { if(Config.ENABLE_ALL_EXCEPTIONS) t.printStackTrace(); }
*/
if (this == _instance)
{
closeServer();
}
else
{
// gm shutdown: send warnings and then call exit to start shutdown sequence
countdown();
if (_shutdownMode != ABORT)
{
// last point where logging is operational :(
LOGGER.warning("GM shutdown countdown is over. " + MODE_TEXT[_shutdownMode] + " NOW!");
closeServer();
}
}
}
/**
* This functions starts a shutdown countdown
* @param activeChar GM who issued the shutdown command
* @param seconds seconds until shutdown
* @param restart true if the server will restart after shutdown
*/
public void startShutdown(L2PcInstance activeChar, int seconds, boolean restart)
{
Announcements _an = Announcements.getInstance();
if (activeChar != null)
{
LOGGER.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
}
else
{
LOGGER.warning("External Service issued shutdown command. " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
}
if (restart)
{
_shutdownMode = GM_RESTART;
}
else
{
_shutdownMode = GM_SHUTDOWN;
}
if (_shutdownMode > 0)
{
_an.announceToAll("Server is " + MODE_TEXT[_shutdownMode] + " in " + seconds + " seconds!");
_an.announceToAll("Please exit game now!!");
}
if (_counterInstance != null)
{
_counterInstance._abort();
}
// the main instance should only run for shutdown hook, so we start a new instance
_counterInstance = new Shutdown(seconds, restart, false, false);
_counterInstance.start();
}
public int getCountdown()
{
return _secondsShut;
}
/**
* This function aborts a running countdown
* @param activeChar GM who issued the abort command
*/
public void abort(L2PcInstance activeChar)
{
Announcements _an = Announcements.getInstance();
if (activeChar != null)
{
LOGGER.warning("GM: " + activeChar.getName() + "(" + activeChar.getObjectId() + ") issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!");
}
else
{
LOGGER.warning("External Service issued shutdown ABORT. " + MODE_TEXT[_shutdownMode] + " has been stopped!");
}
_an.announceToAll("Server aborts " + MODE_TEXT[_shutdownMode] + " and continues normal operation!");
if (_counterInstance != null)
{
_counterInstance._abort();
}
}
/**
* set the shutdown mode
* @param mode what mode shall be set
*/
/*
* private void setMode(int mode) { _shutdownMode = mode; }
*/
/**
* set shutdown mode to ABORT
*/
private void _abort()
{
_shutdownMode = ABORT;
}
/**
* this counts the countdown and reports it to all players countdown is aborted if mode changes to ABORT
*/
/**
* this counts the countdown and reports it to all players countdown is aborted if mode changes to ABORT
*/
private void countdown()
{
try
{
while (_secondsShut > 0)
{
int _seconds;
int _minutes;
int _hours;
_seconds = _secondsShut;
_minutes = _seconds / 60;
_hours = _seconds / 3600;
// announce only every minute after 10 minutes left and every second after 20 seconds
if (((_seconds <= 20) || (_seconds == (_minutes * 10))) && (_seconds <= 600) && (_hours <= 1))
{
SystemMessage sm = new SystemMessage(SystemMessageId.THE_SERVER_WILL_BE_COMING_DOWN_IN_S1_SECONDS);
sm.addString(Integer.toString(_seconds));
Announcements.getInstance().announceToAll(sm);
}
try
{
if (_seconds <= 60)
{
LoginServerThread.getInstance().setServerStatus(ServerStatus.STATUS_DOWN);
}
}
catch (Exception e)
{
// do nothing, we maybe are not connected to LS anymore
}
_secondsShut--;
final int delay = 1000; // milliseconds
Thread.sleep(delay);
if (_shutdownMode == ABORT)
{
break;
}
}
}
catch (InterruptedException e)
{
}
}
private void closeServer()
{
// last byebye, save all data and quit this server
// logging doesnt work here :(
_shutdownStarted = true;
try
{
LoginServerThread.getInstance().interrupt();
}
catch (Throwable t)
{
}
AutoSaveManager.getInstance().stopAutoSaveManager();
// saveData sends messages to exit players, so shutdown selector after it
saveData();
try
{
GameTimeController.getInstance().stopTimer();
}
catch (Throwable t)
{
}
try
{
// GameServer.getSelectorThread().setDaemon(true);
GameServer.getSelectorThread().shutdown();
}
catch (Throwable t)
{
}
// stop all threadpolls
try
{
ThreadPoolManager.shutdown();
}
catch (Throwable t)
{
}
LOGGER.info("Committing all data, last chance...");
// commit data, last chance
try
{
DatabaseFactory.getInstance().close();
}
catch (Throwable t)
{
}
LOGGER.info("All database data committed.");
System.runFinalization();
System.gc();
LOGGER.info("Memory cleanup, recycled unused objects.");
LOGGER.info("[STATUS] Server shutdown successfully.");
// server will quit, when this function ends.
/*
* switch (_shutdownMode) { case GM_SHUTDOWN: _instance.setMode(GM_SHUTDOWN); System.exit(0); break; case GM_RESTART: _instance.setMode(GM_RESTART); System.exit(2); break; case TASK_SHUTDOWN: _instance.setMode(TASK_SHUTDOWN); System.exit(4); break; case TASK_RESTART:
* _instance.setMode(TASK_RESTART); System.exit(5); break; case TELL_SHUTDOWN: _instance.setMode(TELL_SHUTDOWN); System.exit(6); break; case TELL_RESTART: _instance.setMode(TELL_RESTART); System.exit(7); break; }
*/
if (_instance._shutdownMode == GM_RESTART)
{
Runtime.getRuntime().halt(2);
}
else if (_instance._shutdownMode == TASK_RESTART)
{
Runtime.getRuntime().halt(5);
}
else if (_instance._shutdownMode == TASK_SHUTDOWN)
{
Runtime.getRuntime().halt(4);
}
else if (_instance._shutdownMode == TELL_RESTART)
{
Runtime.getRuntime().halt(7);
}
else if (_instance._shutdownMode == TELL_SHUTDOWN)
{
Runtime.getRuntime().halt(6);
}
else
{
Runtime.getRuntime().halt(0);
}
}
/**
* this sends a last byebye, disconnects all players and saves data
*/
private synchronized void saveData()
{
Announcements _an = Announcements.getInstance();
switch (_shutdownMode)
{
case SIGTERM:
{
LOGGER.info("SIGTERM received. Shutting down NOW!");
break;
}
case GM_SHUTDOWN:
{
LOGGER.info("GM shutdown received. Shutting down NOW!");
break;
}
case GM_RESTART:
{
LOGGER.info("GM restart received. Restarting NOW!");
break;
}
case TASK_SHUTDOWN:
{
LOGGER.info("Auto task shutdown received. Shutting down NOW!");
break;
}
case TASK_RESTART:
{
LOGGER.info("Auto task restart received. Restarting NOW!");
break;
}
case TELL_SHUTDOWN:
{
LOGGER.info("Telnet shutdown received. Shutting down NOW!");
break;
}
case TELL_RESTART:
{
LOGGER.info("Telnet restart received. Restarting NOW!");
break;
}
}
try
{
_an.announceToAll("Server is " + MODE_TEXT[_shutdownMode] + " NOW!");
}
catch (Throwable t)
{
}
try
{
if ((Config.OFFLINE_TRADE_ENABLE || Config.OFFLINE_CRAFT_ENABLE) && Config.RESTORE_OFFLINERS)
{
OfflineTradeTable.storeOffliners();
}
}
catch (Throwable t)
{
LOGGER.warning("Error saving offline shops. " + t);
}
try
{
wait(1000);
}
catch (InterruptedException e1)
{
}
// Disconnect all the players from the server
disconnectAllCharacters();
try
{
wait(5000);
}
catch (InterruptedException e1)
{
}
// Save players data!
saveAllPlayers();
try
{
wait(10000);
}
catch (InterruptedException e1)
{
}
// Seven Signs data is now saved along with Festival data.
if (!SevenSigns.getInstance().isSealValidationPeriod())
{
SevenSignsFestival.getInstance().saveFestivalData(false);
}
// Save Seven Signs data before closing. :)
SevenSigns.getInstance().saveSevenSignsData(null, true);
LOGGER.info("SevenSigns: All info saved!!");
// Save all raidboss status
RaidBossSpawnManager.getInstance().cleanUp();
LOGGER.info("RaidBossSpawnManager: All raidboss info saved!!");
// Save all Grandboss status
GrandBossManager.getInstance().cleanUp();
LOGGER.info("GrandBossManager: All Grand Boss info saved!!");
// Save data CountStore
TradeController.getInstance().dataCountStore();
LOGGER.info("TradeController: All count Item Saved");
// Save Olympiad status
try
{
Olympiad.getInstance().saveOlympiadStatus();
}
catch (Exception e)
{
e.printStackTrace();
}
LOGGER.info("Olympiad System: Data saved!!");
// Save Cursed Weapons data before closing.
CursedWeaponsManager.getInstance().saveData();
// Save all manor data
CastleManorManager.getInstance().save();
// Save Fishing tournament data
FishingChampionshipManager.getInstance().shutdown();
LOGGER.info("Fishing Championship data has been saved.");
// Schemes save.
BufferTable.getInstance().saveSchemes();
LOGGER.info("BufferTable data has been saved.");
// Save all global (non-player specific) Quest data that needs to persist after reboot
if (!Config.ALT_DEV_NO_QUESTS)
{
QuestManager.getInstance().save();
}
// Save items on ground before closing
if (Config.SAVE_DROPPED_ITEM)
{
ItemsOnGroundManager.getInstance().saveInDb();
ItemsOnGroundManager.getInstance().cleanUp();
LOGGER.info("ItemsOnGroundManager: All items on ground saved!!");
}
try
{
wait(5000);
}
catch (InterruptedException e)
{
// never happens :p
}
}
private void saveAllPlayers()
{
LOGGER.info("Saving all players data...");
for (L2PcInstance player : L2World.getInstance().getAllPlayers())
{
if (player == null)
{
continue;
}
// Logout Character
try
{
// Save player status
player.store();
}
catch (Throwable t)
{
}
}
}
/**
* this disconnects all clients from the server
*/
private void disconnectAllCharacters()
{
LOGGER.info("Disconnecting all players from the Server...");
for (L2PcInstance player : L2World.getInstance().getAllPlayers())
{
if (player == null)
{
continue;
}
try
{
// Player Disconnect
if (player.getClient() != null)
{
player.getClient().sendPacket(ServerClose.STATIC_PACKET);
player.getClient().close(0);
player.getClient().setActiveChar(null);
player.setClient(null);
}
}
catch (Throwable t)
{
}
}
}
}

View File

@ -0,0 +1,687 @@
/*
* 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.gameserver;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.LineNumberReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.datatables.xml.ItemTable;
import com.l2jmobius.gameserver.model.L2TradeList;
import com.l2jmobius.gameserver.model.actor.instance.L2ItemInstance;
/**
* This class ...
* @version $Revision: 1.5.4.13 $ $Date: 2005/04/06 16:13:38 $
*/
public class TradeController
{
private static Logger LOGGER = Logger.getLogger(TradeController.class.getName());
private static TradeController _instance;
private int _nextListId;
private final Map<Integer, L2TradeList> _lists;
private final Map<Integer, L2TradeList> _listsTaskItem;
/** Task launching the function for restore count of Item (Clan Hall) */
public class RestoreCount implements Runnable
{
private final int _timer;
public RestoreCount(int time)
{
_timer = time;
}
@Override
public void run()
{
try
{
restoreCount(_timer);
dataTimerSave(_timer);
ThreadPoolManager.schedule(new RestoreCount(_timer), (long) _timer * 60 * 60 * 1000);
}
catch (Throwable t)
{
}
}
}
public static TradeController getInstance()
{
if (_instance == null)
{
_instance = new TradeController();
}
return _instance;
}
private TradeController()
{
_lists = new HashMap<>();
_listsTaskItem = new HashMap<>();
final File buylistData = new File(Config.DATAPACK_ROOT, "data/buylists.csv");
if (buylistData.exists())
{
LOGGER.warning("Do, please, remove buylists from data folder and use SQL buylist instead");
String line = null;
int dummyItemCount = 0;
FileReader reader = null;
BufferedReader buff = null;
LineNumberReader lnr = null;
try
{
reader = new FileReader(buylistData);
buff = new BufferedReader(reader);
lnr = new LineNumberReader(buff);
while ((line = lnr.readLine()) != null)
{
if ((line.trim().length() == 0) || line.startsWith("#"))
{
continue;
}
dummyItemCount += parseList(line);
}
if (Config.DEBUG)
{
LOGGER.info("created " + dummyItemCount + " Dummy-Items for buylists");
}
LOGGER.info("TradeController: Loaded " + _lists.size() + " Buylists.");
}
catch (Exception e)
{
LOGGER.warning("error while creating trade controller in line: " + (lnr == null ? 0 : lnr.getLineNumber()) + " " + e);
}
finally
{
if (lnr != null)
{
try
{
lnr.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (buff != null)
{
try
{
buff.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (reader != null)
{
try
{
reader.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
else
{
LOGGER.info("No buylists found in data folder, using SQL buylist instead.");
/**
* Initialize Shop buylist
*/
int dummyItemCount = 0;
boolean LimitedItem = false;
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement1 = con.prepareStatement("SELECT * FROM merchant_shopids");
ResultSet rset1 = statement1.executeQuery();
while (rset1.next())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM merchant_buylists WHERE shop_id=? ORDER BY `order` ASC");
statement.setString(1, String.valueOf(rset1.getInt("shop_id")));
ResultSet rset = statement.executeQuery();
if (rset.next())
{
LimitedItem = false;
dummyItemCount++;
L2TradeList buy1 = new L2TradeList(rset1.getInt("shop_id"));
int itemId = rset.getInt("item_id");
int price = rset.getInt("price");
int count = rset.getInt("count");
int currentCount = rset.getInt("currentCount");
int time = rset.getInt("time");
L2ItemInstance item = ItemTable.getInstance().createDummyItem(itemId);
if (item == null)
{
rset.close();
statement.close();
continue;
}
if (count > -1)
{
item.setCountDecrease(true);
LimitedItem = true;
}
if (!rset1.getString("npc_id").equals("gm") && (price < (item.getReferencePrice() / 2)))
{
LOGGER.warning("L2TradeList " + buy1.getListId() + " itemId " + itemId + " has an ADENA sell price lower then reference price.. Automatically Updating it..");
price = item.getReferencePrice();
}
item.setPriceToSell(price);
item.setTime(time);
item.setInitCount(count);
if (currentCount > -1)
{
item.setCount(currentCount);
}
else
{
item.setCount(count);
}
buy1.addItem(item);
buy1.setNpcId(rset1.getString("npc_id"));
try
{
while (rset.next()) // TODO aici
{
dummyItemCount++;
itemId = rset.getInt("item_id");
price = rset.getInt("price");
count = rset.getInt("count");
time = rset.getInt("time");
currentCount = rset.getInt("currentCount");
final L2ItemInstance item2 = ItemTable.getInstance().createDummyItem(itemId);
if (item2 == null)
{
continue;
}
if (count > -1)
{
item2.setCountDecrease(true);
LimitedItem = true;
}
if (!rset1.getString("npc_id").equals("gm") && (price < (item2.getReferencePrice() / 2)))
{
LOGGER.warning("L2TradeList " + buy1.getListId() + " itemId " + itemId + " has an ADENA sell price lower then reference price.. Automatically Updating it..");
price = item2.getReferencePrice();
}
item2.setPriceToSell(price);
item2.setTime(time);
item2.setInitCount(count);
if (currentCount > -1)
{
item2.setCount(currentCount);
}
else
{
item2.setCount(count);
}
buy1.addItem(item2);
}
}
catch (Exception e)
{
LOGGER.warning("TradeController: Problem with buylist " + buy1.getListId() + " item " + itemId);
}
if (LimitedItem)
{
_listsTaskItem.put(new Integer(buy1.getListId()), buy1);
}
else
{
_lists.put(new Integer(buy1.getListId()), buy1);
}
_nextListId = Math.max(_nextListId, buy1.getListId() + 1);
}
rset.close();
statement.close();
}
rset1.close();
statement1.close();
if (Config.DEBUG)
{
LOGGER.info("created " + dummyItemCount + " Dummy-Items for buylists");
}
LOGGER.info("TradeController: Loaded " + _lists.size() + " Buylists.");
LOGGER.info("TradeController: Loaded " + _listsTaskItem.size() + " Limited Buylists.");
/*
* Restore Task for reinitialyze count of buy item
*/
try
{
int time = 0;
long savetimer = 0;
final long currentMillis = System.currentTimeMillis();
PreparedStatement statement2 = con.prepareStatement("SELECT DISTINCT time, savetimer FROM merchant_buylists WHERE time <> 0 ORDER BY time");
ResultSet rset2 = statement2.executeQuery();
while (rset2.next())
{
time = rset2.getInt("time");
savetimer = rset2.getLong("savetimer");
if ((savetimer - currentMillis) > 0)
{
ThreadPoolManager.schedule(new RestoreCount(time), savetimer - System.currentTimeMillis());
}
else
{
ThreadPoolManager.schedule(new RestoreCount(time), 0);
}
}
rset2.close();
statement2.close();
}
catch (Exception e)
{
LOGGER.warning("TradeController: Could not restore Timer for Item count.");
e.printStackTrace();
}
}
catch (Exception e)
{
// problem with initializing spawn, go to next one
LOGGER.warning("TradeController: Buylists could not be initialized.");
e.printStackTrace();
}
/*
* If enabled, initialize the custom buylist
*/
if (Config.CUSTOM_MERCHANT_TABLES)// Custom merchat Tabels
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
final int initialSize = _lists.size();
PreparedStatement statement1 = con.prepareStatement("SELECT * FROM custom_merchant_shopids");
ResultSet rset1 = statement1.executeQuery();
while (rset1.next())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM custom_merchant_buylists WHERE shop_id=? ORDER BY `order` ASC");
statement.setString(1, String.valueOf(rset1.getInt("shop_id")));
ResultSet rset = statement.executeQuery();
if (rset.next())
{
LimitedItem = false;
dummyItemCount++;
L2TradeList buy1 = new L2TradeList(rset1.getInt("shop_id"));
int itemId = rset.getInt("item_id");
int price = rset.getInt("price");
int count = rset.getInt("count");
int currentCount = rset.getInt("currentCount");
int time = rset.getInt("time");
L2ItemInstance item = ItemTable.getInstance().createDummyItem(itemId);
if (item == null)
{
rset.close();
statement.close();
continue;
}
if (count > -1)
{
item.setCountDecrease(true);
LimitedItem = true;
}
if (!rset1.getString("npc_id").equals("gm") && (price < (item.getReferencePrice() / 2)))
{
LOGGER.warning("L2TradeList " + buy1.getListId() + " itemId " + itemId + " has an ADENA sell price lower then reference price.. Automatically Updating it..");
price = item.getReferencePrice();
}
item.setPriceToSell(price);
item.setTime(time);
item.setInitCount(count);
if (currentCount > -1)
{
item.setCount(currentCount);
}
else
{
item.setCount(count);
}
buy1.addItem(item);
buy1.setNpcId(rset1.getString("npc_id"));
try
{
while (rset.next())
{
dummyItemCount++;
itemId = rset.getInt("item_id");
price = rset.getInt("price");
count = rset.getInt("count");
time = rset.getInt("time");
currentCount = rset.getInt("currentCount");
L2ItemInstance item2 = ItemTable.getInstance().createDummyItem(itemId);
if (item2 == null)
{
continue;
}
if (count > -1)
{
item2.setCountDecrease(true);
LimitedItem = true;
}
if (!rset1.getString("npc_id").equals("gm") && (price < (item2.getReferencePrice() / 2)))
{
LOGGER.warning("L2TradeList " + buy1.getListId() + " itemId " + itemId + " has an ADENA sell price lower then reference price.. Automatically Updating it..");
price = item2.getReferencePrice();
}
item2.setPriceToSell(price);
item2.setTime(time);
item2.setInitCount(count);
if (currentCount > -1)
{
item2.setCount(currentCount);
}
else
{
item2.setCount(count);
}
buy1.addItem(item2);
}
}
catch (Exception e)
{
LOGGER.warning("TradeController: Problem with buylist " + buy1.getListId() + " item " + itemId);
}
if (LimitedItem)
{
_listsTaskItem.put(new Integer(buy1.getListId()), buy1);
}
else
{
_lists.put(new Integer(buy1.getListId()), buy1);
}
_nextListId = Math.max(_nextListId, buy1.getListId() + 1);
}
rset.close();
statement.close();
}
rset1.close();
statement1.close();
if (Config.DEBUG)
{
LOGGER.info("created " + dummyItemCount + " Dummy-Items for buylists");
}
LOGGER.info("TradeController: Loaded " + (_lists.size() - initialSize) + " Custom Buylists.");
/**
* Restore Task for reinitialyze count of buy item
*/
try
{
int time = 0;
long savetimer = 0;
final long currentMillis = System.currentTimeMillis();
PreparedStatement statement2 = con.prepareStatement("SELECT DISTINCT time, savetimer FROM custom_merchant_buylists WHERE time <> 0 ORDER BY time");
ResultSet rset2 = statement2.executeQuery();
while (rset2.next())
{
time = rset2.getInt("time");
savetimer = rset2.getLong("savetimer");
if ((savetimer - currentMillis) > 0)
{
ThreadPoolManager.schedule(new RestoreCount(time), savetimer - System.currentTimeMillis());
}
else
{
ThreadPoolManager.schedule(new RestoreCount(time), 0);
}
}
rset2.close();
statement2.close();
}
catch (Exception e)
{
LOGGER.warning("TradeController: Could not restore Timer for Item count.");
e.printStackTrace();
}
}
catch (Exception e)
{
// problem with initializing spawn, go to next one
LOGGER.warning("TradeController: Buylists could not be initialized.");
e.printStackTrace();
}
}
}
}
private int parseList(String line)
{
int itemCreated = 0;
StringTokenizer st = new StringTokenizer(line, ";");
final int listId = Integer.parseInt(st.nextToken());
L2TradeList buy1 = new L2TradeList(listId);
while (st.hasMoreTokens())
{
final int itemId = Integer.parseInt(st.nextToken());
int price = Integer.parseInt(st.nextToken());
final L2ItemInstance item = ItemTable.getInstance().createDummyItem(itemId);
if (price < (item.getReferencePrice() / 2))
{
LOGGER.warning("L2TradeList " + listId + " itemId " + itemId + " has an ADENA sell price lower then reference price.. Automatically Updating it..");
price = item.getReferencePrice();
}
item.setPriceToSell(price);
buy1.addItem(item);
itemCreated++;
}
_lists.put(new Integer(buy1.getListId()), buy1);
return itemCreated;
}
public L2TradeList getBuyList(int listId)
{
if (_lists.get(new Integer(listId)) != null)
{
return _lists.get(new Integer(listId));
}
return _listsTaskItem.get(new Integer(listId));
}
public List<L2TradeList> getBuyListByNpcId(int npcId)
{
final List<L2TradeList> lists = new ArrayList<>();
for (L2TradeList list : _lists.values())
{
if (list.getNpcId().startsWith("gm"))
{
continue;
}
if (npcId == Integer.parseInt(list.getNpcId()))
{
lists.add(list);
}
}
for (L2TradeList list : _listsTaskItem.values())
{
if (list.getNpcId().startsWith("gm"))
{
continue;
}
if (npcId == Integer.parseInt(list.getNpcId()))
{
lists.add(list);
}
}
return lists;
}
protected void restoreCount(int time)
{
if (_listsTaskItem == null)
{
return;
}
for (L2TradeList list : _listsTaskItem.values())
{
list.restoreCount(time);
}
}
protected void dataTimerSave(int time)
{
final long timerSave = System.currentTimeMillis() + ((long) time * 60 * 60 * 1000);
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("UPDATE merchant_buylists SET savetimer =? WHERE time =?");
statement.setLong(1, timerSave);
statement.setInt(2, time);
statement.executeUpdate();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("TradeController: Could not update Timer save in Buylist");
}
}
public void dataCountStore()
{
int listId;
if (_listsTaskItem == null)
{
return;
}
PreparedStatement statement;
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
for (L2TradeList list : _listsTaskItem.values())
{
if (list == null)
{
continue;
}
listId = list.getListId();
for (L2ItemInstance Item : list.getItems())
{
if (Item.getCount() < Item.getInitCount()) // needed?
{
statement = con.prepareStatement("UPDATE merchant_buylists SET currentCount=? WHERE item_id=? AND shop_id=?");
statement.setInt(1, Item.getCount());
statement.setInt(2, Item.getItemId());
statement.setInt(3, listId);
statement.executeUpdate();
statement.close();
}
}
}
}
catch (Exception e)
{
LOGGER.warning("TradeController: Could not store Count Item");
}
}
/**
* @return
*/
public synchronized int getNextId()
{
return _nextListId++;
}
/**
* This will reload buylists info from DataBase
*/
public static void reload()
{
_instance = new TradeController();
}
}

View File

@ -0,0 +1,993 @@
/*
* 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.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.concurrent.Future;
import java.util.logging.Logger;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Playable;
import com.l2jmobius.gameserver.model.actor.L2Summon;
import com.l2jmobius.gameserver.model.actor.instance.L2NpcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.actor.position.Location;
import com.l2jmobius.gameserver.model.extender.BaseExtender.EventType;
import com.l2jmobius.gameserver.network.serverpackets.ActionFailed;
import com.l2jmobius.gameserver.network.serverpackets.AutoAttackStart;
import com.l2jmobius.gameserver.network.serverpackets.AutoAttackStop;
import com.l2jmobius.gameserver.network.serverpackets.CharMoveToLocation;
import com.l2jmobius.gameserver.network.serverpackets.Die;
import com.l2jmobius.gameserver.network.serverpackets.MoveToLocationInVehicle;
import com.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
import com.l2jmobius.gameserver.network.serverpackets.StopMove;
import com.l2jmobius.gameserver.network.serverpackets.StopRotation;
import com.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
/**
* Mother class of all objects AI in the world.<BR>
* <BR>
* AbastractAI :<BR>
* <BR>
* <li>L2CharacterAI</li><BR>
* <BR>
*/
abstract class AbstractAI implements Ctrl
{
protected static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
class FollowTask implements Runnable
{
protected int _range = 60;
protected boolean newtask = true;
public FollowTask()
{
// null
}
public FollowTask(int range)
{
_range = range;
}
@Override
public void run()
{
try
{
if (_followTask == null)
{
return;
}
final L2Character follow = getFollowTarget();
if (follow == null)
{
stopFollow();
return;
}
if (!_actor.isInsideRadius(follow, _range, true, false))
{
moveToPawn(follow, _range);
}
else if (newtask)
{
newtask = false;
_actor.broadcastPacket(new MoveToPawn(_actor, follow, _range));
}
}
catch (Throwable t)
{
LOGGER.warning(t.getMessage());
}
}
}
/** The character that this AI manages */
protected final L2Character _actor;
/** An accessor for private methods of the actor */
protected final L2Character.AIAccessor _accessor;
/** Current long-term intention */
private CtrlIntention _intention = AI_INTENTION_IDLE;
/** Current long-term intention parameter */
private Object _intentionArg0 = null;
/** Current long-term intention parameter */
private Object _intentionArg1 = null;
/** Flags about client's state, in order to know which messages to send */
protected boolean _clientMoving;
/** Flags about client's state, in order to know which messages to send */
protected boolean _clientAutoAttacking;
/** Flags about client's state, in order to know which messages to send */
protected int _clientMovingToPawnOffset;
/** Different targets this AI maintains */
private L2Object _target;
private L2Character _castTarget;
private L2Character _attackTarget;
private L2Character _followTarget;
/** Diferent internal state flags */
private int _moveToPawnTimeout;
protected Future<?> _followTask = null;
private static final int FOLLOW_INTERVAL = 1000;
private static final int ATTACK_FOLLOW_INTERVAL = 500;
/**
* Constructor of AbstractAI.<BR>
* <BR>
* @param accessor The AI accessor of the L2Character
*/
protected AbstractAI(L2Character.AIAccessor accessor)
{
_accessor = accessor;
// Get the L2Character managed by this Accessor AI
_actor = accessor.getActor();
}
/**
* Return the L2Character managed by this Accessor AI.<BR>
* <BR>
*/
@Override
public L2Character getActor()
{
return _actor;
}
/**
* Set the Intention of this AbstractAI.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : This method is USED by AI classes</B></FONT><BR>
* <BR>
* <B><U> Overriden in </U> : </B><BR>
* <B>L2AttackableAI</B> : Create an AI Task executed every 1s (if necessary)<BR>
* <B>L2PlayerAI</B> : Stores the current AI intention parameters to later restore it if necessary<BR>
* <BR>
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention
* @param arg1 The second parameter of the Intention
*/
public synchronized void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
_intention = intention;
_intentionArg0 = arg0;
_intentionArg1 = arg1;
}
/**
* Launch the L2CharacterAI onIntention method corresponding to the new Intention.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Stop the FOLLOW mode if necessary</B></FONT><BR>
* <BR>
* @param intention The new Intention to set to the AI
*/
@Override
public final void setIntention(CtrlIntention intention)
{
setIntention(intention, null, null);
}
/**
* Launch the L2CharacterAI onIntention method corresponding to the new Intention.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Stop the FOLLOW mode if necessary</B></FONT><BR>
* <BR>
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention (optional target)
*/
@Override
public final void setIntention(CtrlIntention intention, Object arg0)
{
setIntention(intention, arg0, null);
}
/**
* Launch the L2CharacterAI onIntention method corresponding to the new Intention.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Stop the FOLLOW mode if necessary</B></FONT><BR>
* <BR>
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention (optional target)
* @param arg1 The second parameter of the Intention (optional target)
*/
@Override
public final void setIntention(CtrlIntention intention, Object arg0, Object arg1)
{
if (!_actor.isVisible() || !_actor.hasAI())
{
return;
}
// Stop the follow mode if necessary
if ((intention != AI_INTENTION_FOLLOW) && (intention != AI_INTENTION_ATTACK))
{
stopFollow();
}
// Launch the onIntention method of the L2CharacterAI corresponding to the new Intention
switch (intention)
{
case AI_INTENTION_IDLE:
{
onIntentionIdle();
break;
}
case AI_INTENTION_ACTIVE:
{
onIntentionActive();
break;
}
case AI_INTENTION_REST:
{
onIntentionRest();
break;
}
case AI_INTENTION_ATTACK:
{
onIntentionAttack((L2Character) arg0);
break;
}
case AI_INTENTION_CAST:
{
onIntentionCast((L2Skill) arg0, (L2Object) arg1);
break;
}
case AI_INTENTION_MOVE_TO:
{
onIntentionMoveTo((Location) arg0);
break;
}
case AI_INTENTION_MOVE_TO_IN_A_BOAT:
{
onIntentionMoveToInABoat((Location) arg0, (Location) arg1);
break;
}
case AI_INTENTION_FOLLOW:
{
onIntentionFollow((L2Character) arg0);
break;
}
case AI_INTENTION_PICK_UP:
{
onIntentionPickUp((L2Object) arg0);
break;
}
case AI_INTENTION_INTERACT:
{
onIntentionInteract((L2Object) arg0);
break;
}
}
_actor.fireEvent(EventType.SETINTENTION.name, new Object[]
{
intention
});
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned periode)</B></FONT><BR>
* <BR>
* @param evt The event whose the AI must be notified
*/
@Override
public final void notifyEvent(CtrlEvent evt)
{
notifyEvent(evt, null, null);
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned periode)</B></FONT><BR>
* <BR>
* @param evt The event whose the AI must be notified
* @param arg0 The first parameter of the Event (optional target)
*/
@Override
public final void notifyEvent(CtrlEvent evt, Object arg0)
{
notifyEvent(evt, arg0, null);
}
/**
* Launch the L2CharacterAI onEvt method corresponding to the Event.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned periode)</B></FONT><BR>
* <BR>
* @param evt The event whose the AI must be notified
* @param arg0 The first parameter of the Event (optional target)
* @param arg1 The second parameter of the Event (optional target)
*/
@Override
public final void notifyEvent(CtrlEvent evt, Object arg0, Object arg1)
{
if (!_actor.isVisible() || !_actor.hasAI() || ((_actor instanceof L2PcInstance) && (((L2PcInstance) _actor).isOnline() == 0)) || ((_actor instanceof L2PcInstance) && ((L2PcInstance) _actor).isInOfflineMode()))
{
return;
}
/*
* if (Config.DEBUG) LOGGER.warning("AbstractAI: notifyEvent -> " + evt + " " + arg0 + " " + arg1);
*/
switch (evt)
{
case EVT_THINK:
{
onEvtThink();
break;
}
case EVT_ATTACKED:
{
onEvtAttacked((L2Character) arg0);
break;
}
case EVT_AGGRESSION:
{
onEvtAggression((L2Character) arg0, ((Number) arg1).intValue());
break;
}
case EVT_STUNNED:
{
onEvtStunned((L2Character) arg0);
break;
}
case EVT_SLEEPING:
{
onEvtSleeping((L2Character) arg0);
break;
}
case EVT_ROOTED:
{
onEvtRooted((L2Character) arg0);
break;
}
case EVT_CONFUSED:
{
onEvtConfused((L2Character) arg0);
break;
}
case EVT_MUTED:
{
onEvtMuted((L2Character) arg0);
break;
}
case EVT_READY_TO_ACT:
{
onEvtReadyToAct();
break;
}
case EVT_USER_CMD:
{
onEvtUserCmd(arg0, arg1);
break;
}
case EVT_ARRIVED:
{
onEvtArrived();
break;
}
case EVT_ARRIVED_REVALIDATE:
{
onEvtArrivedRevalidate();
break;
}
case EVT_ARRIVED_BLOCKED:
{
onEvtArrivedBlocked((Location) arg0);
break;
}
case EVT_FORGET_OBJECT:
{
onEvtForgetObject((L2Object) arg0);
break;
}
case EVT_CANCEL:
{
onEvtCancel();
break;
}
case EVT_DEAD:
{
onEvtDead();
break;
}
case EVT_FAKE_DEATH:
{
onEvtFakeDeath();
break;
}
case EVT_FINISH_CASTING:
{
onEvtFinishCasting();
break;
}
}
}
protected abstract void onIntentionIdle();
protected abstract void onIntentionActive();
protected abstract void onIntentionRest();
protected abstract void onIntentionAttack(L2Character target);
protected abstract void onIntentionCast(L2Skill skill, L2Object target);
protected abstract void onIntentionMoveTo(Location destination);
protected abstract void onIntentionMoveToInABoat(Location destination, Location origin);
protected abstract void onIntentionFollow(L2Character target);
protected abstract void onIntentionPickUp(L2Object item);
protected abstract void onIntentionInteract(L2Object object);
protected abstract void onEvtThink();
protected abstract void onEvtAttacked(L2Character attacker);
protected abstract void onEvtAggression(L2Character target, int aggro);
protected abstract void onEvtStunned(L2Character attacker);
protected abstract void onEvtSleeping(L2Character attacker);
protected abstract void onEvtRooted(L2Character attacker);
protected abstract void onEvtConfused(L2Character attacker);
protected abstract void onEvtMuted(L2Character attacker);
protected abstract void onEvtReadyToAct();
protected abstract void onEvtUserCmd(Object arg0, Object arg1);
protected abstract void onEvtArrived();
protected abstract void onEvtArrivedRevalidate();
protected abstract void onEvtArrivedBlocked(Location blocked_at_pos);
protected abstract void onEvtForgetObject(L2Object object);
protected abstract void onEvtCancel();
protected abstract void onEvtDead();
protected abstract void onEvtFakeDeath();
protected abstract void onEvtFinishCasting();
/**
* Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
*/
protected void clientActionFailed()
{
if (_actor instanceof L2PcInstance)
{
_actor.sendPacket(ActionFailed.STATIC_PACKET);
}
}
/**
* Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn <I>(broadcast)</I>.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
* @param pawn
* @param offset
*/
public void moveToPawn(L2Object pawn, int offset)
{
// Chek if actor can move
if (!_actor.isMovementDisabled())
{
if (offset < 10)
{
offset = 10;
}
// prevent possible extra calls to this function (there is none?),
// also don't send movetopawn packets too often
boolean sendPacket = true;
if (_clientMoving && (getTarget() == pawn))
{
if (_clientMovingToPawnOffset == offset)
{
if (GameTimeController.getGameTicks() < _moveToPawnTimeout)
{
return;
}
sendPacket = false;
}
else if (_actor.isOnGeodataPath())
{
// minimum time to calculate new route is 2 seconds
if (GameTimeController.getGameTicks() < (_moveToPawnTimeout + 10))
{
return;
}
}
}
// Set AI movement data
_clientMoving = true;
_clientMovingToPawnOffset = offset;
setTarget(pawn);
_moveToPawnTimeout = GameTimeController.getGameTicks();
_moveToPawnTimeout += /* 1000 */200 / GameTimeController.MILLIS_IN_TICK;
if ((pawn == null) || (_accessor == null))
{
return;
}
// Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController
_accessor.moveTo(pawn.getX(), pawn.getY(), pawn.getZ(), offset);
if (!_actor.isMoving())
{
_actor.sendPacket(ActionFailed.STATIC_PACKET);
return;
}
// Send a Server->Client packet MoveToPawn/CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
if (pawn instanceof L2Character)
{
if (_actor.isOnGeodataPath())
{
_actor.broadcastPacket(new CharMoveToLocation(_actor));
_clientMovingToPawnOffset = 0;
}
else if (sendPacket)
{
_actor.broadcastPacket(new MoveToPawn(_actor, (L2Character) pawn, offset));
}
}
else
{
_actor.broadcastPacket(new CharMoveToLocation(_actor));
}
}
else
{
_actor.sendPacket(ActionFailed.STATIC_PACKET);
}
}
/**
* Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation <I>(broadcast)</I>.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
* @param x
* @param y
* @param z
*/
public void moveTo(int x, int y, int z)
{
// Chek if actor can move
if (!_actor.isMovementDisabled())
{
// Set AI movement data
_clientMoving = true;
_clientMovingToPawnOffset = 0;
// Calculate movement data for a move to location action and add the actor to movingObjects of GameTimeController
_accessor.moveTo(x, y, z);
// Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
CharMoveToLocation msg = new CharMoveToLocation(_actor);
_actor.broadcastPacket(msg);
msg = null;
}
else
{
_actor.sendPacket(ActionFailed.STATIC_PACKET);
}
}
protected void moveToInABoat(Location destination, Location origin)
{
// Chek if actor can move
if (!_actor.isMovementDisabled())
{
// Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
// CharMoveToLocation msg = new CharMoveToLocation(_actor);
if (((L2PcInstance) _actor).getBoat() != null)
{
MoveToLocationInVehicle msg = new MoveToLocationInVehicle(_actor, destination, origin);
_actor.broadcastPacket(msg);
msg = null;
}
}
else
{
_actor.sendPacket(ActionFailed.STATIC_PACKET);
}
}
/**
* Stop the actor movement server side AND client side by sending Server->Client packet StopMove/StopRotation <I>(broadcast)</I>.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
* @param pos
*/
protected void clientStopMoving(Location pos)
{
/*
* if (true && _actor instanceof L2PcInstance){ LOGGER.warning("clientStopMoving();"); Thread.dumpStack(); }
*/
// Stop movement of the L2Character
if (_actor.isMoving())
{
_accessor.stopMove(pos);
}
_clientMovingToPawnOffset = 0;
if (_clientMoving || (pos != null))
{
_clientMoving = false;
// Send a Server->Client packet StopMove to the actor and all L2PcInstance in its _knownPlayers
StopMove msg = new StopMove(_actor);
_actor.broadcastPacket(msg);
msg = null;
if (pos != null)
{
// Send a Server->Client packet StopRotation to the actor and all L2PcInstance in its _knownPlayers
StopRotation sr = new StopRotation(_actor, pos.getHeading(), 0);
_actor.sendPacket(sr);
_actor.broadcastPacket(sr);
sr = null;
}
}
}
// Client has already arrived to target, no need to force StopMove packet
protected void clientStoppedMoving()
{
if (_clientMovingToPawnOffset > 0) // movetoPawn needs to be stopped
{
_clientMovingToPawnOffset = 0;
StopMove msg = new StopMove(_actor);
_actor.broadcastPacket(msg);
msg = null;
}
_clientMoving = false;
}
/**
* Start the actor Auto Attack client side by sending Server->Client packet AutoAttackStart <I>(broadcast)</I>.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
*/
/*
* public void clientStartAutoAttack() { if(!isAutoAttacking()) { // Send a Server->Client packet AutoAttackStart to the actor and all L2PcInstance in its _knownPlayers _actor.broadcastPacket(new AutoAttackStart(_actor.getObjectId())); setAutoAttacking(true); }
* AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor); }
*/
/**
* Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop <I>(broadcast)</I>.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
*/
/*
* public void clientStopAutoAttack() { if(_actor instanceof L2PcInstance) { if(!AttackStanceTaskManager.getInstance().getAttackStanceTask(_actor) && isAutoAttacking()) { AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor); } } else if(isAutoAttacking()) {
* _actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId())); } setAutoAttacking(false); }
*/
/**
* Start the actor Auto Attack client side by sending Server->Client packet AutoAttackStart <I>(broadcast)</I>.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
*/
public void clientStartAutoAttack()
{
if ((((_actor instanceof L2NpcInstance) && !(_actor instanceof L2Attackable)) && !(_actor instanceof L2Playable)))
{
return;
}
if (_actor instanceof L2Summon)
{
final L2Summon summon = (L2Summon) _actor;
if (summon.getOwner() != null)
{
summon.getOwner().getAI().clientStartAutoAttack();
}
return;
}
if (!isAutoAttacking())
{
if ((_actor instanceof L2PcInstance) && (((L2PcInstance) _actor).getPet() != null))
{
((L2PcInstance) _actor).getPet().broadcastPacket(new AutoAttackStart(((L2PcInstance) _actor).getPet().getObjectId()));
}
// Send a Server->Client packet AutoAttackStart to the actor and all L2PcInstance in its _knownPlayers
_actor.broadcastPacket(new AutoAttackStart(_actor.getObjectId()));
setAutoAttacking(true);
}
AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor);
}
/**
* Stop the actor auto-attack client side by sending Server->Client packet AutoAttackStop <I>(broadcast)</I>.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
*/
public void clientStopAutoAttack()
{
if (_actor instanceof L2Summon)
{
final L2Summon summon = (L2Summon) _actor;
if (summon.getOwner() != null)
{
summon.getOwner().getAI().clientStopAutoAttack();
}
return;
}
final boolean isAutoAttacking = isAutoAttacking();
if (_actor instanceof L2PcInstance)
{
if (!AttackStanceTaskManager.getInstance().getAttackStanceTask(_actor) && isAutoAttacking)
{
AttackStanceTaskManager.getInstance().addAttackStanceTask(_actor);
}
}
else if (isAutoAttacking)
{
_actor.broadcastPacket(new AutoAttackStop(_actor.getObjectId()));
setAutoAttacking(false);
}
}
/**
* Kill the actor client side by sending Server->Client packet AutoAttackStop, StopMove/StopRotation, Die <I>(broadcast)</I>.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
*/
protected void clientNotifyDead()
{
// Send a Server->Client packet Die to the actor and all L2PcInstance in its _knownPlayers
Die msg = new Die(_actor);
_actor.broadcastPacket(msg);
msg = null;
// Init AI
setIntention(AI_INTENTION_IDLE);
setTarget(null);
setAttackTarget(null);
setCastTarget(null);
// Cancel the follow task if necessary
stopFollow();
}
/**
* Update the state of this actor client side by sending Server->Client packet MoveToPawn/CharMoveToLocation and AutoAttackStart to the L2PcInstance player.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT><BR>
* <BR>
* @param player The L2PcIstance to notify with state of this L2Character
*/
public void describeStateToPlayer(L2PcInstance player)
{
if (_clientMoving)
{
final L2Character follow = getFollowTarget();
if ((_clientMovingToPawnOffset != 0) && (follow != null))
{
// Send a Server->Client packet MoveToPawn to the actor and all L2PcInstance in its _knownPlayers
MoveToPawn msg = new MoveToPawn(_actor, follow, _clientMovingToPawnOffset);
player.sendPacket(msg);
msg = null;
}
else
{
// Send a Server->Client packet CharMoveToLocation to the actor and all L2PcInstance in its _knownPlayers
CharMoveToLocation msg = new CharMoveToLocation(_actor);
player.sendPacket(msg);
msg = null;
}
}
}
/**
* Create and Launch an AI Follow Task to execute every 1s.<BR>
* <BR>
* @param target The L2Character to follow
*/
public synchronized void startFollow(L2Character target)
{
if (_followTask != null)
{
_followTask.cancel(false);
_followTask = null;
}
// Create and Launch an AI Follow Task to execute every 1s
_followTarget = target;
_followTask = ThreadPoolManager.scheduleAtFixedRate(new FollowTask(), 5, FOLLOW_INTERVAL);
}
/**
* Create and Launch an AI Follow Task to execute every 0.5s, following at specified range.<BR>
* <BR>
* @param target The L2Character to follow
* @param range
*/
public synchronized void startFollow(L2Character target, int range)
{
if (_followTask != null)
{
_followTask.cancel(false);
_followTask = null;
}
_followTarget = target;
_followTask = ThreadPoolManager.scheduleAtFixedRate(new FollowTask(range), 5, ATTACK_FOLLOW_INTERVAL);
}
/**
* Stop an AI Follow Task.<BR>
* <BR>
*/
public synchronized void stopFollow()
{
if (_followTask != null)
{
// Stop the Follow Task
_followTask.cancel(false);
_followTask = null;
}
_followTarget = null;
}
protected synchronized L2Character getFollowTarget()
{
return _followTarget;
}
protected synchronized L2Object getTarget()
{
return _target;
}
protected synchronized void setTarget(L2Object target)
{
_target = target;
}
protected synchronized void setCastTarget(L2Character target)
{
_castTarget = target;
}
/**
* @return the current cast target.
*/
public synchronized L2Character getCastTarget()
{
return _castTarget;
}
protected synchronized void setAttackTarget(L2Character target)
{
_attackTarget = target;
}
/**
* Return current attack target.<BR>
* <BR>
*/
@Override
public synchronized L2Character getAttackTarget()
{
return _attackTarget;
}
public synchronized boolean isAutoAttacking()
{
return _clientAutoAttacking;
}
public synchronized void setAutoAttacking(boolean isAutoAttacking)
{
_clientAutoAttacking = isAutoAttacking;
}
/**
* @return the _intentionArg0
*/
public synchronized Object get_intentionArg0()
{
return _intentionArg0;
}
/**
* @param _intentionArg0 the _intentionArg0 to set
*/
public synchronized void set_intentionArg0(Object _intentionArg0)
{
this._intentionArg0 = _intentionArg0;
}
/**
* @return the _intentionArg1
*/
public synchronized Object get_intentionArg1()
{
return _intentionArg1;
}
/**
* @param _intentionArg1 the _intentionArg1 to set
*/
public synchronized void set_intentionArg1(Object _intentionArg1)
{
this._intentionArg1 = _intentionArg1;
}
/**
* Return the current Intention.<BR>
* <BR>
*/
@Override
public synchronized CtrlIntention getIntention()
{
return _intention;
}
}

View File

@ -0,0 +1,66 @@
/*
* 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.gameserver.ai;
import com.l2jmobius.gameserver.model.actor.L2Character;
/**
* Interface of AI and client state. To correctly send messages to client we need it's state. For example, if we've sent 'StartAutoAttack' message, we need to send 'StopAutoAttack' message before any other action. Or if we've sent 'MoveToPawn', we need to send 'StopMove' when the movement of a
* character is canceled (by Root spell or any other reason). Thus, we need to know the state of client, i.e. which messages we've sent and how the client will show the scene. Close to this task is the task of AI. If a player's character is attacking a mob, his ATTACK may be interrupted by an event,
* that temporary disable attacking. But when the possibility to ATTACK will be enabled, the character must continue the ATTACK. For mobs it may be more complex, since we want them to decide when to use magic, or when to follow the player for physical combat, or when to escape, to help another mob,
* etc. This interface is hiding complexity of server<->client interaction and multiple states of a character. It allows to set a desired, simple "wish" of a character, and the implementation of this interface will take care about the rest. The goal of a character may be like "ATTACK", "random walk"
* and so on. To reach the goal implementation will split it into several small actions, several steps (possibly repeatable). Like "run to target" then "hit it", then if target is not dead - repeat. This flow of simpler steps may be interrupted by incoming events. Like a character's movement was
* disabled (by Root spell, for instance). Depending on character's ability AI may choose to wait, or to use magic ATTACK and so on. Additionally incoming events are compared with client's state of the character, and required network messages are sent to client's, i.e. if we have incoming event that
* character's movement was disabled, it causes changing if its Behavior, and if client's state for the character is "moving" we send messages to clients to stop the avatar/mob.
*/
public interface Ctrl
{
/**
* @return the character this AI serves
*/
L2Character getActor();
/**
* @return the current intention.
*/
CtrlIntention getIntention();
/**
* @return the current attack target.
*/
L2Character getAttackTarget();
/**
* Set general state/intention for AI, with optional data
* @param intention
*/
void setIntention(CtrlIntention intention);
void setIntention(CtrlIntention intention, Object arg0);
void setIntention(CtrlIntention intention, Object arg0, Object arg1);
/**
* Event, that notifies about previous step result, or user command, that does not change current general intention
* @param evt
*/
void notifyEvent(CtrlEvent evt);
void notifyEvent(CtrlEvent evt, Object arg0);
void notifyEvent(CtrlEvent evt, Object arg0, Object arg1);
}

View File

@ -0,0 +1,99 @@
/*
* 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.gameserver.ai;
/**
* This class contains an enum of each possibles evenements that can happen on an AI character.
*/
public enum CtrlEvent
{
/**
* Something has changed, usually a previous step has being completed or maybe was completed, the AI must thing on next action
*/
EVT_THINK,
/**
* The actor was attacked. This event comes each time a physical or magical attack was done on the actor. NPC may start attack in responce, or ignore this event if they already attack someone, or change target and so on.
*/
EVT_ATTACKED,
/** Increase/decrease aggression towards a target, or reduce global aggression if target is null */
EVT_AGGRESSION,
/** Actor is in stun state */
EVT_STUNNED,
/** Actor starts/stops sleeping */
EVT_SLEEPING,
/** Actor is in rooted state (cannot move) */
EVT_ROOTED,
/**
* An event that previous action was completed. The action may be an attempt to physically/magically hit an enemy, or an action that discarded attack attempt has finished.
*/
EVT_READY_TO_ACT,
/**
* User's command, like using a combat magic or changing weapon, etc. The command is not intended to change final goal
*/
EVT_USER_CMD,
/**
* The actor arrived to assigned location, or it's a time to modify movement destination (follow, interact, random move and others intentions).
*/
EVT_ARRIVED,
/**
* The actor arrived to an intermidiate point, and needs revalidate destination. This is sent when follow/move to pawn if destination is far away.
*/
EVT_ARRIVED_REVALIDATE,
/** The actor cannot move anymore. */
EVT_ARRIVED_BLOCKED,
/** Forgets an object (if it's used as attack target, follow target and so on */
EVT_FORGET_OBJECT,
/**
* Attempt to cancel current step execution, but not change the intention. For example, the actor was putted into a stun, so it's current attack or movement has to be canceled. But after the stun state expired, the actor may try to attack again. Another usage for CANCEL is a user's attempt to
* cancel a cast/bow attack and so on.
*/
EVT_CANCEL,
/** The character is dead */
EVT_DEAD,
/** The character looks like dead */
EVT_FAKE_DEATH,
/** The character attack anyone randomly **/
EVT_CONFUSED,
/** The character cannot cast spells anymore **/
EVT_MUTED,
/** The character flee in randoms directions **/
EVT_AFFRAID,
/** The character finish casting **/
EVT_FINISH_CASTING,
/** The character betrayed its master */
EVT_BETRAYED
}

View File

@ -0,0 +1,56 @@
/*
* 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.gameserver.ai;
/**
* Enumaration of generic intentions of an NPC/PC, an intention may require several steps to be completed
*/
public enum CtrlIntention
{
/** Do nothing, disconnect AI of NPC if no players around */
AI_INTENTION_IDLE,
/** Alerted state without goal : scan attackable targets, random walk, etc */
AI_INTENTION_ACTIVE,
/** Rest (sit until attacked) */
AI_INTENTION_REST,
/**
* Attack target (cast combat magic, go to target, combat), may be ignored, if target is locked on another character or a peacefull zone and so on
*/
AI_INTENTION_ATTACK,
/** Cast a spell, depending on the spell - may start or stop attacking */
AI_INTENTION_CAST,
/** Just move to another location */
AI_INTENTION_MOVE_TO,
/** Like move, but check target's movement and follow it */
AI_INTENTION_FOLLOW,
/** PickUp and item, (got to item, pickup it, become idle */
AI_INTENTION_PICK_UP,
/** Move to target, then interact */
AI_INTENTION_INTERACT,
/** Move to another location in a boat */
AI_INTENTION_MOVE_TO_IN_A_BOAT
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,573 @@
/*
* 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.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import java.util.ArrayList;
import java.util.List;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.datatables.MobGroupTable;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.model.MobGroup;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Character.AIAccessor;
import com.l2jmobius.gameserver.model.actor.instance.L2ControllableMobInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2FolkInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2NpcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.util.Util;
/**
* @author littlecrow AI for controllable mobs
*/
public class L2ControllableMobAI extends L2AttackableAI
{
public static final int AI_IDLE = 1;
public static final int AI_NORMAL = 2;
public static final int AI_FORCEATTACK = 3;
public static final int AI_FOLLOW = 4;
public static final int AI_CAST = 5;
public static final int AI_ATTACK_GROUP = 6;
private int _alternateAI;
private boolean _isThinking; // to prevent thinking recursively
private boolean _isNotMoving;
private L2Character _forcedTarget;
private MobGroup _targetGroup;
protected void thinkFollow()
{
final L2Attackable me = (L2Attackable) _actor;
if (!Util.checkIfInRange(MobGroupTable.FOLLOW_RANGE, me, getForcedTarget(), true))
{
final int signX = Rnd.nextInt(2) == 0 ? -1 : 1;
final int signY = Rnd.nextInt(2) == 0 ? -1 : 1;
final int randX = Rnd.nextInt(MobGroupTable.FOLLOW_RANGE);
final int randY = Rnd.nextInt(MobGroupTable.FOLLOW_RANGE);
moveTo(getForcedTarget().getX() + (signX * randX), getForcedTarget().getY() + (signY * randY), getForcedTarget().getZ());
}
}
@Override
protected void onEvtThink()
{
if (isThinking() || _actor.isAllSkillsDisabled())
{
return;
}
setThinking(true);
try
{
switch (getAlternateAI())
{
case AI_IDLE:
{
if (getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)
{
setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
}
break;
}
case AI_FOLLOW:
{
thinkFollow();
break;
}
case AI_CAST:
{
thinkCast();
break;
}
case AI_FORCEATTACK:
{
thinkForceAttack();
break;
}
case AI_ATTACK_GROUP:
{
thinkAttackGroup();
break;
}
default:
{
if (getIntention() == AI_INTENTION_ACTIVE)
{
thinkActive();
}
else if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
break;
}
}
}
finally
{
setThinking(false);
}
}
protected void thinkCast()
{
if ((getAttackTarget() == null) || getAttackTarget().isAlikeDead())
{
setAttackTarget(findNextRndTarget());
clientStopMoving(null);
}
if (getAttackTarget() == null)
{
return;
}
((L2Attackable) _actor).setTarget(getAttackTarget());
if (!_actor.isMuted())
{
// check distant skills
int max_range = 0;
for (L2Skill sk : _actor.getAllSkills())
{
if (Util.checkIfInRange(sk.getCastRange(), _actor, getAttackTarget(), true) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
max_range = Math.max(max_range, sk.getCastRange());
}
if (!isNotMoving())
{
moveToPawn(getAttackTarget(), max_range);
}
return;
}
}
protected void thinkAttackGroup()
{
final L2Character target = getForcedTarget();
if ((target == null) || target.isAlikeDead())
{
// try to get next group target
setForcedTarget(findNextGroupTarget());
clientStopMoving(null);
}
if (target == null)
{
return;
}
_actor.setTarget(target);
// as a response, we put the target in a forced attack mode
final L2ControllableMobInstance theTarget = (L2ControllableMobInstance) target;
final L2ControllableMobAI ctrlAi = (L2ControllableMobAI) theTarget.getAI();
ctrlAi.forceAttack(_actor);
final L2Skill[] skills = _actor.getAllSkills();
final double dist2 = _actor.getPlanDistanceSq(target.getX(), target.getY());
final int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + target.getTemplate().collisionRadius;
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (L2Skill sk : skills)
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
if (!isNotMoving())
{
moveToPawn(target, range);
}
return;
}
_accessor.doAttack(target);
}
protected void thinkForceAttack()
{
if ((getForcedTarget() == null) || getForcedTarget().isAlikeDead())
{
clientStopMoving(null);
setIntention(AI_INTENTION_ACTIVE);
setAlternateAI(AI_IDLE);
}
_actor.setTarget(getForcedTarget());
final L2Skill[] skills = _actor.getAllSkills();
final double dist2 = _actor.getPlanDistanceSq(getForcedTarget().getX(), getForcedTarget().getY());
final int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + getForcedTarget().getTemplate().collisionRadius;
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (L2Skill sk : skills)
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
if (!isNotMoving())
{
moveToPawn(getForcedTarget(), _actor.getPhysicalAttackRange()/* range */);
}
return;
}
_accessor.doAttack(getForcedTarget());
}
protected void thinkAttack()
{
if ((getAttackTarget() == null) || getAttackTarget().isAlikeDead())
{
if (getAttackTarget() != null)
{
// stop hating
L2Attackable npc = (L2Attackable) _actor;
npc.stopHating(getAttackTarget());
}
setIntention(AI_INTENTION_ACTIVE);
}
else
{
// notify aggression
if (((L2NpcInstance) _actor).getFactionId() != null)
{
for (L2Object obj : _actor.getKnownList().getKnownObjects().values())
{
if (!(obj instanceof L2NpcInstance))
{
continue;
}
L2NpcInstance npc = (L2NpcInstance) obj;
String faction_id = ((L2NpcInstance) _actor).getFactionId();
if (!faction_id.equalsIgnoreCase(npc.getFactionId()))
{
continue;
}
if (_actor.isInsideRadius(npc, npc.getFactionRange(), false, true) && (Math.abs(getAttackTarget().getZ() - npc.getZ()) < 200))
{
npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1);
}
}
}
_actor.setTarget(getAttackTarget());
final L2Skill[] skills = _actor.getAllSkills();
final double dist2 = _actor.getPlanDistanceSq(getAttackTarget().getX(), getAttackTarget().getY());
final int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius;
int max_range = range;
if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
{
// check distant skills
for (L2Skill sk : skills)
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
max_range = Math.max(max_range, castRange);
}
moveToPawn(getAttackTarget(), range);
return;
}
// Force mobs to attack anybody if confused.
L2Character hated;
if (_actor.isConfused())
{
hated = findNextRndTarget();
}
else
{
hated = getAttackTarget();
}
if (hated == null)
{
setIntention(AI_INTENTION_ACTIVE);
return;
}
if (hated != getAttackTarget())
{
setAttackTarget(hated);
}
if (!_actor.isMuted() && (skills.length > 0) && (Rnd.nextInt(5) == 3))
{
for (L2Skill sk : skills)
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)))
{
_accessor.doCast(sk);
return;
}
}
}
_accessor.doAttack(getAttackTarget());
}
}
private void thinkActive()
{
setAttackTarget(findNextRndTarget());
L2Character hated;
if (_actor.isConfused())
{
hated = findNextRndTarget();
}
else
{
hated = getAttackTarget();
}
if (hated != null)
{
_actor.setRunning();
setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated);
}
return;
}
private boolean autoAttackCondition(L2Character target)
{
if ((target == null) || !(_actor instanceof L2Attackable))
{
return false;
}
final L2Attackable me = (L2Attackable) _actor;
if ((target instanceof L2FolkInstance) || (target instanceof L2DoorInstance))
{
return false;
}
if (target.isAlikeDead() || !me.isInsideRadius(target, me.getAggroRange(), false, false) || (Math.abs(_actor.getZ() - target.getZ()) > 100))
{
return false;
}
// Check if the target isn't invulnerable
if (target.isInvul())
{
return false;
}
// Check if the target is a L2PcInstance
if (target instanceof L2PcInstance)
{
// Check if the target isn't in silent move mode
if (((L2PcInstance) target).isSilentMoving())
{
return false;
}
}
if (target instanceof L2NpcInstance)
{
return false;
}
return me.isAggressive();
}
private L2Character findNextRndTarget()
{
final int aggroRange = ((L2Attackable) _actor).getAggroRange();
L2Attackable npc = (L2Attackable) _actor;
int npcX, npcY, targetX, targetY;
double dy, dx;
final double dblAggroRange = aggroRange * aggroRange;
final List<L2Character> potentialTarget = new ArrayList<>();
for (L2Object obj : npc.getKnownList().getKnownObjects().values())
{
if (!(obj instanceof L2Character))
{
continue;
}
npcX = npc.getX();
npcY = npc.getY();
targetX = obj.getX();
targetY = obj.getY();
dx = npcX - targetX;
dy = npcY - targetY;
if (((dx * dx) + (dy * dy)) > dblAggroRange)
{
continue;
}
final L2Character target = (L2Character) obj;
if (autoAttackCondition(target))
{
potentialTarget.add(target);
}
}
if (potentialTarget.size() == 0)
{
return null;
}
// we choose a random target
final int choice = Rnd.nextInt(potentialTarget.size());
final L2Character target = potentialTarget.get(choice);
return target;
}
private L2ControllableMobInstance findNextGroupTarget()
{
return getGroupTarget().getRandomMob();
}
public L2ControllableMobAI(AIAccessor accessor)
{
super(accessor);
setAlternateAI(AI_IDLE);
}
public int getAlternateAI()
{
return _alternateAI;
}
public void setAlternateAI(int _alternateai)
{
_alternateAI = _alternateai;
}
public void forceAttack(L2Character target)
{
setAlternateAI(AI_FORCEATTACK);
setForcedTarget(target);
}
public void forceAttackGroup(MobGroup group)
{
setForcedTarget(null);
setGroupTarget(group);
setAlternateAI(AI_ATTACK_GROUP);
}
public void stop()
{
setAlternateAI(AI_IDLE);
clientStopMoving(null);
}
public void move(int x, int y, int z)
{
moveTo(x, y, z);
}
public void follow(L2Character target)
{
setAlternateAI(AI_FOLLOW);
setForcedTarget(target);
}
public boolean isThinking()
{
return _isThinking;
}
public boolean isNotMoving()
{
return _isNotMoving;
}
public void setNotMoving(boolean isNotMoving)
{
_isNotMoving = isNotMoving;
}
public void setThinking(boolean isThinking)
{
_isThinking = isThinking;
}
private synchronized L2Character getForcedTarget()
{
return _forcedTarget;
}
private synchronized MobGroup getGroupTarget()
{
return _targetGroup;
}
private synchronized void setForcedTarget(L2Character forcedTarget)
{
_forcedTarget = forcedTarget;
}
private synchronized void setGroupTarget(MobGroup targetGroup)
{
_targetGroup = targetGroup;
}
}

View File

@ -0,0 +1,214 @@
/*
* 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.gameserver.ai;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2FortSiegeGuardInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2SiegeGuardInstance;
import com.l2jmobius.gameserver.model.actor.position.Location;
/**
* @author mkizub TODO To change the template for this generated type comment go to Window - Preferences - Java - Code Style - Code Templates
*/
public class L2DoorAI extends L2CharacterAI
{
public L2DoorAI(L2DoorInstance.AIAccessor accessor)
{
super(accessor);
}
// rather stupid AI... well, it's for doors :D
@Override
protected void onIntentionIdle()
{
// null;
}
@Override
protected void onIntentionActive()
{
// null;
}
@Override
protected void onIntentionRest()
{
// null;
}
@Override
protected void onIntentionAttack(L2Character target)
{
// null;
}
@Override
protected void onIntentionCast(L2Skill skill, L2Object target)
{
// null;
}
@Override
protected void onIntentionMoveTo(Location destination)
{
// null;
}
@Override
protected void onIntentionFollow(L2Character target)
{
// null;
}
@Override
protected void onIntentionPickUp(L2Object item)
{
// null;
}
@Override
protected void onIntentionInteract(L2Object object)
{
// null;
}
@Override
protected void onEvtThink()
{
// null;
}
@Override
protected void onEvtAttacked(L2Character attacker)
{
L2DoorInstance me = (L2DoorInstance) _actor;
ThreadPoolManager.execute(new onEventAttackedDoorTask(me, attacker));
}
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
// null;
}
@Override
protected void onEvtStunned(L2Character attacker)
{
// null;
}
@Override
protected void onEvtSleeping(L2Character attacker)
{
// null;
}
@Override
protected void onEvtRooted(L2Character attacker)
{
// null;
}
@Override
protected void onEvtReadyToAct()
{
// null;
}
@Override
protected void onEvtUserCmd(Object arg0, Object arg1)
{
// null;
}
@Override
protected void onEvtArrived()
{
// null;
}
@Override
protected void onEvtArrivedRevalidate()
{
// null;
}
@Override
protected void onEvtArrivedBlocked(Location blocked_at_pos)
{
// null;
}
@Override
protected void onEvtForgetObject(L2Object object)
{
// null;
}
@Override
protected void onEvtCancel()
{
// null;
}
@Override
protected void onEvtDead()
{
// null;
}
private class onEventAttackedDoorTask implements Runnable
{
private final L2DoorInstance _door;
private final L2Character _attacker;
public onEventAttackedDoorTask(L2DoorInstance door, L2Character attacker)
{
_door = door;
_attacker = attacker;
}
/*
* (non-Javadoc)
* @see java.lang.Runnable#run()
*/
@Override
public void run()
{
_door.getKnownList().updateKnownObjects();
for (L2SiegeGuardInstance guard : _door.getKnownSiegeGuards())
{
if ((guard != null) && (guard.getAI() != null) && _actor.isInsideRadius(guard, guard.getFactionRange(), false, true) && (Math.abs(_attacker.getZ() - guard.getZ()) < 200))
{
guard.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, _attacker, 15);
}
}
for (L2FortSiegeGuardInstance guard : _door.getKnownFortSiegeGuards())
{
if ((guard != null) && (guard.getAI() != null) && _actor.isInsideRadius(guard, guard.getFactionRange(), false, true) && (Math.abs(_attacker.getZ() - guard.getZ()) < 200))
{
guard.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, _attacker, 15);
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,244 @@
/*
* 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.gameserver.ai;
import java.util.List;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.gameserver.datatables.csv.NpcWalkerRoutesTable;
import com.l2jmobius.gameserver.model.L2NpcWalkerNode;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.instance.L2NpcWalkerInstance;
import com.l2jmobius.gameserver.model.actor.position.Location;
public class L2NpcWalkerAI extends L2CharacterAI implements Runnable
{
private static final int DEFAULT_MOVE_DELAY = 0;
private long _nextMoveTime;
private boolean _walkingToNextPoint = false;
/**
* home points for xyz
*/
int _homeX, _homeY, _homeZ;
/**
* route of the current npc
*/
private final List<L2NpcWalkerNode> _route = NpcWalkerRoutesTable.getInstance().getRouteForNpc(getActor().getNpcId());
/**
* current node
*/
private int _currentPos;
/**
* Constructor of L2CharacterAI.<BR>
* <BR>
* @param accessor The AI accessor of the L2Character
*/
public L2NpcWalkerAI(L2Character.AIAccessor accessor)
{
super(accessor);
// Do we really need 2 minutes delay before start?
// no we dont... :)
ThreadPoolManager.scheduleAtFixedRate(this, 0, 1000);
}
@Override
public void run()
{
onEvtThink();
}
@Override
protected void onEvtThink()
{
if (!Config.ALLOW_NPC_WALKERS)
{
return;
}
if (isWalkingToNextPoint())
{
checkArrived();
return;
}
if (_nextMoveTime < System.currentTimeMillis())
{
walkToLocation();
}
}
/**
* If npc can't walk to it's target then just teleport to next point
* @param blocked_at_pos ignoring it
*/
@Override
protected void onEvtArrivedBlocked(Location blocked_at_pos)
{
LOGGER.warning("NpcWalker ID: " + getActor().getNpcId() + ": Blocked at rote position [" + _currentPos + "], coords: " + blocked_at_pos.getX() + ", " + blocked_at_pos.getY() + ", " + blocked_at_pos.getZ() + ". Teleporting to next point");
if (_route.size() <= _currentPos)
{
return;
}
final int destinationX = _route.get(_currentPos).getMoveX();
final int destinationY = _route.get(_currentPos).getMoveY();
final int destinationZ = _route.get(_currentPos).getMoveZ();
getActor().teleToLocation(destinationX, destinationY, destinationZ, false);
super.onEvtArrivedBlocked(blocked_at_pos);
}
private void checkArrived()
{
if (_route.size() <= _currentPos)
{
return;
}
final int destinationX = _route.get(_currentPos).getMoveX();
final int destinationY = _route.get(_currentPos).getMoveY();
final int destinationZ = _route.get(_currentPos).getMoveZ();
if ((getActor().getX() == destinationX) && (getActor().getY() == destinationY) && (getActor().getZ() == destinationZ))
{
String chat = _route.get(_currentPos).getChatText();
if ((chat != null) && !chat.equals("NULL"))
{
try
{
getActor().broadcastChat(chat);
}
catch (ArrayIndexOutOfBoundsException e)
{
LOGGER.info("L2NpcWalkerInstance: Error, " + e);
}
}
// time in millis
long delay = _route.get(_currentPos).getDelay() * 1000;
// sleeps between each move
if (delay < 0)
{
delay = DEFAULT_MOVE_DELAY;
if (Config.DEVELOPER)
{
LOGGER.warning("Wrong Delay Set in Npc Walker Functions = " + delay + " secs, using default delay: " + DEFAULT_MOVE_DELAY + " secs instead.");
}
}
_nextMoveTime = System.currentTimeMillis() + delay;
setWalkingToNextPoint(false);
}
}
private void walkToLocation()
{
if (_currentPos < (_route.size() - 1))
{
_currentPos++;
}
else
{
_currentPos = 0;
}
if (_route.size() <= _currentPos)
{
return;
}
final boolean moveType = _route.get(_currentPos).getRunning();
/**
* false - walking true - Running
*/
if (moveType)
{
getActor().setRunning();
}
else
{
getActor().setWalking();
}
// now we define destination
final int destinationX = _route.get(_currentPos).getMoveX();
final int destinationY = _route.get(_currentPos).getMoveY();
final int destinationZ = _route.get(_currentPos).getMoveZ();
// notify AI of MOVE_TO
setWalkingToNextPoint(true);
setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new Location(destinationX, destinationY, destinationZ, 0));
}
@Override
public L2NpcWalkerInstance getActor()
{
return (L2NpcWalkerInstance) super.getActor();
}
public int getHomeX()
{
return _homeX;
}
public int getHomeY()
{
return _homeY;
}
public int getHomeZ()
{
return _homeZ;
}
public void setHomeX(int homeX)
{
_homeX = homeX;
}
public void setHomeY(int homeY)
{
_homeY = homeY;
}
public void setHomeZ(int homeZ)
{
_homeZ = homeZ;
}
public boolean isWalkingToNextPoint()
{
return _walkingToNextPoint;
}
public void setWalkingToNextPoint(boolean value)
{
_walkingToNextPoint = value;
}
}

View File

@ -0,0 +1,378 @@
/*
* 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.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_CAST;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_INTERACT;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_PICK_UP;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_REST;
import java.util.EmptyStackException;
import java.util.Stack;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Character.AIAccessor;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2StaticObjectInstance;
import com.l2jmobius.gameserver.model.actor.knownlist.ObjectKnownList.KnownListAsynchronousUpdateTask;
import com.l2jmobius.gameserver.model.actor.position.Location;
public class L2PlayerAI extends L2CharacterAI
{
private boolean _thinking; // to prevent recursive thinking
class IntentionCommand
{
protected CtrlIntention _crtlIntention;
protected Object _arg0, _arg1;
protected IntentionCommand(CtrlIntention pIntention, Object pArg0, Object pArg1)
{
_crtlIntention = pIntention;
_arg0 = pArg0;
_arg1 = pArg1;
}
}
private final Stack<IntentionCommand> _interuptedIntentions = new Stack<>();
private synchronized Stack<IntentionCommand> getInterruptedIntentions()
{
return _interuptedIntentions;
}
public L2PlayerAI(AIAccessor accessor)
{
super(accessor);
}
/**
* Saves the current Intention for this L2PlayerAI if necessary and calls changeIntention in AbstractAI.<BR>
* <BR>
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention
* @param arg1 The second parameter of the Intention
*/
@Override
public void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
/*
* if (Config.DEBUG) LOGGER.warning("L2PlayerAI: changeIntention -> " + intention + " " + arg0 + " " + arg1);
*/
// nothing to do if it does not CAST intention
if (intention != AI_INTENTION_CAST)
{
super.changeIntention(intention, arg0, arg1);
return;
}
final CtrlIntention _intention = getIntention();
final Object _intentionArg0 = get_intentionArg0();
final Object _intentionArg1 = get_intentionArg1();
// do nothing if next intention is same as current one.
if ((intention == _intention) && (arg0 == _intentionArg0) && (arg1 == _intentionArg1))
{
super.changeIntention(intention, arg0, arg1);
return;
}
/*
* if (Config.DEBUG) LOGGER.warning("L2PlayerAI: changeIntention -> Saving current intention: " + _intention + " " + _intention_arg0 + " " + _intention_arg1);
*/
// push current intention to stack
getInterruptedIntentions().push(new IntentionCommand(_intention, _intentionArg0, _intentionArg1));
super.changeIntention(intention, arg0, arg1);
}
/**
* Finalize the casting of a skill. This method overrides L2CharacterAI method.<BR>
* <BR>
* <B>What it does:</B> Check if actual intention is set to CAST and, if so, retrieves latest intention before the actual CAST and set it as the current intention for the player
*/
@Override
protected void onEvtFinishCasting()
{
// forget interupted actions after offensive skill
final L2Skill skill = get_skill();
if ((skill != null) && skill.isOffensive())
{
getInterruptedIntentions().clear();
}
if (getIntention() == AI_INTENTION_CAST)
{
// run interupted intention if it remain.
if (!getInterruptedIntentions().isEmpty())
{
IntentionCommand cmd = null;
try
{
cmd = getInterruptedIntentions().pop();
}
catch (EmptyStackException ese)
{
}
/*
* if (Config.DEBUG) LOGGER.warning("L2PlayerAI: onEvtFinishCasting -> " + cmd._intention + " " + cmd._arg0 + " " + cmd._arg1);
*/
if ((cmd != null) && (cmd._crtlIntention != AI_INTENTION_CAST)) // previous state shouldn't be casting
{
setIntention(cmd._crtlIntention, cmd._arg0, cmd._arg1);
}
else
{
setIntention(AI_INTENTION_IDLE);
}
}
else
{
/*
* if (Config.DEBUG) LOGGER.warning("L2PlayerAI: no previous intention set... Setting it to IDLE");
*/
// set intention to idle if skill doesn't change intention.
setIntention(AI_INTENTION_IDLE);
}
}
}
@Override
protected void onIntentionRest()
{
if (getIntention() != AI_INTENTION_REST)
{
changeIntention(AI_INTENTION_REST, null, null);
setTarget(null);
if (getAttackTarget() != null)
{
setAttackTarget(null);
}
clientStopMoving(null);
}
}
@Override
protected void clientStopMoving(Location pos)
{
super.clientStopMoving(pos);
final L2PcInstance _player = (L2PcInstance) _actor;
if (_player.getPosticipateSit())
{
_player.sitDown();
}
}
@Override
protected void onIntentionActive()
{
setIntention(AI_INTENTION_IDLE);
}
@Override
protected void clientNotifyDead()
{
_clientMovingToPawnOffset = 0;
_clientMoving = false;
super.clientNotifyDead();
}
private void thinkAttack()
{
final L2Character target = getAttackTarget();
if (target == null)
{
return;
}
if (checkTargetLostOrDead(target))
{
// Notify the target
setAttackTarget(null);
return;
}
if (maybeMoveToPawn(target, _actor.getPhysicalAttackRange()))
{
return;
}
_accessor.doAttack(target);
return;
}
private void thinkCast()
{
final L2Character target = getCastTarget();
final L2Skill skill = get_skill();
// if (Config.DEBUG) LOGGER.warning("L2PlayerAI: thinkCast -> Start");
if (checkTargetLost(target))
{
if (skill.isOffensive() && (getAttackTarget() != null))
{
// Notify the target
setCastTarget(null);
}
return;
}
if (target != null)
{
if (maybeMoveToPawn(target, _actor.getMagicalAttackRange(skill)))
{
return;
}
}
if (skill.getHitTime() > 50)
{
clientStopMoving(null);
}
final L2Object oldTarget = _actor.getTarget();
if (oldTarget != null)
{
// Replace the current target by the cast target
if ((target != null) && (oldTarget != target))
{
_actor.setTarget(getCastTarget());
}
// Launch the Cast of the skill
_accessor.doCast(get_skill());
// Restore the initial target
if ((target != null) && (oldTarget != target))
{
_actor.setTarget(oldTarget);
}
}
else
{
_accessor.doCast(skill);
}
return;
}
private void thinkPickUp()
{
if (_actor.isAllSkillsDisabled())
{
return;
}
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
((L2PcInstance.AIAccessor) _accessor).doPickupItem(target);
return;
}
private void thinkInteract()
{
if (_actor.isAllSkillsDisabled())
{
return;
}
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
if (!(target instanceof L2StaticObjectInstance))
{
((L2PcInstance.AIAccessor) _accessor).doInteract((L2Character) target);
}
setIntention(AI_INTENTION_IDLE);
return;
}
@Override
protected void onEvtThink()
{
if (_thinking || _actor.isAllSkillsDisabled())
{
return;
}
_thinking = true;
try
{
if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
else if (getIntention() == AI_INTENTION_CAST)
{
thinkCast();
}
else if (getIntention() == AI_INTENTION_PICK_UP)
{
thinkPickUp();
}
else if (getIntention() == AI_INTENTION_INTERACT)
{
thinkInteract();
}
}
finally
{
_thinking = false;
}
}
@Override
protected void onEvtArrivedRevalidate()
{
ThreadPoolManager.execute(new KnownListAsynchronousUpdateTask(_actor));
super.onEvtArrivedRevalidate();
}
}

View File

@ -0,0 +1,866 @@
/*
* 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.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import java.util.concurrent.Future;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.commons.util.Rnd;
import com.l2jmobius.gameserver.GameTimeController;
import com.l2jmobius.gameserver.geodata.GeoData;
import com.l2jmobius.gameserver.model.L2Effect;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Summon;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2FolkInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2NpcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2SiegeGuardInstance;
/**
* This class manages AI of L2Attackable.<BR>
* <BR>
*/
public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
{
// protected static final Logger LOGGER = Logger.getLogger(L2SiegeGuardAI.class);
private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
/** The L2Attackable AI task executed every 1s (call onEvtThink method) */
private Future<?> _aiTask;
/** The delay after wich the attacked is stopped */
private int _attackTimeout;
/** The L2Attackable aggro counter */
private int _globalAggro;
/** The flag used to indicate that a thinking action is in progress */
private boolean _thinking; // to prevent recursive thinking
private final int _attackRange;
/**
* Constructor of L2AttackableAI.<BR>
* <BR>
* @param accessor The AI accessor of the L2Character
*/
public L2SiegeGuardAI(L2Character.AIAccessor accessor)
{
super(accessor);
_attackTimeout = Integer.MAX_VALUE;
_globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
_attackRange = ((L2Attackable) _actor).getPhysicalAttackRange();
}
@Override
public void run()
{
// Launch actions corresponding to the Event Think
onEvtThink();
}
/**
* Return True if the target is autoattackable (depends on the actor type).<BR>
* <BR>
* <B><U> Actor is a L2GuardInstance</U> :</B><BR>
* <BR>
* <li>The target isn't a Folk or a Door</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The L2PcInstance target has karma (=PK)</li>
* <li>The L2MonsterInstance target is aggressive</li><BR>
* <BR>
* <B><U> Actor is a L2SiegeGuardInstance</U> :</B><BR>
* <BR>
* <li>The target isn't a Folk or a Door</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>A siege is in progress</li>
* <li>The L2PcInstance target isn't a Defender</li> <BR>
* <BR>
* <B><U> Actor is a L2FriendlyMobInstance</U> :</B><BR>
* <BR>
* <li>The target isn't a Folk, a Door or another L2NpcInstance</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The L2PcInstance target has karma (=PK)</li><BR>
* <BR>
* <B><U> Actor is a L2MonsterInstance</U> :</B><BR>
* <BR>
* <li>The target isn't a Folk, a Door or another L2NpcInstance</li>
* <li>The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)</li>
* <li>The target is in the actor Aggro range and is at the same height</li>
* <li>The actor is Aggressive</li><BR>
* <BR>
* @param target The targeted L2Object
* @return
*/
private boolean autoAttackCondition(L2Character target)
{
// Check if the target isn't another guard, folk or a door
if ((target == null) || (target instanceof L2SiegeGuardInstance) || (target instanceof L2FolkInstance) || (target instanceof L2DoorInstance) || target.isAlikeDead() || target.isInvul())
{
return false;
}
// Get the owner if the target is a summon
if (target instanceof L2Summon)
{
L2PcInstance owner = ((L2Summon) target).getOwner();
if (_actor.isInsideRadius(owner, 1000, true, false))
{
target = owner;
}
}
// Check if the target is a L2PcInstance
if (target instanceof L2PcInstance)
{
// Check if the target isn't in silent move mode AND too far (>100)
if (((L2PcInstance) target).isSilentMoving() && !_actor.isInsideRadius(target, 250, false, false))
{
return false;
}
}
// Los Check Here
return _actor.isAutoAttackable(target) && GeoData.getInstance().canSeeTarget(_actor, target);
}
/**
* Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.<BR>
* <BR>
* <FONT COLOR=#FF0000><B> <U>Caution</U> : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE</B></FONT><BR>
* <BR>
* @param intention The new Intention to set to the AI
* @param arg0 The first parameter of the Intention
* @param arg1 The second parameter of the Intention
*/
@Override
public void changeIntention(CtrlIntention intention, Object arg0, Object arg1)
{
if (Config.DEBUG)
{
LOGGER.info("L2SiegeAI.changeIntention(" + intention + ", " + arg0 + ", " + arg1 + ")");
}
((L2Attackable) _actor).setisReturningToSpawnPoint(false);
if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
{
// Check if actor is not dead
if (!_actor.isAlikeDead())
{
L2Attackable npc = (L2Attackable) _actor;
// If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
if (npc.getKnownList().getKnownPlayers().size() > 0)
{
intention = AI_INTENTION_ACTIVE;
}
else
{
intention = AI_INTENTION_IDLE;
}
}
if (intention == AI_INTENTION_IDLE)
{
// Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
super.changeIntention(AI_INTENTION_IDLE, null, null);
// Stop AI task and detach AI from NPC
if (_aiTask != null)
{
_aiTask.cancel(true);
_aiTask = null;
}
// Cancel the AI
_accessor.detachAI();
return;
}
}
// Set the Intention of this L2AttackableAI to intention
super.changeIntention(intention, arg0, arg1);
// If not idle - create an AI task (schedule onEvtThink repeatedly)
if (_aiTask == null)
{
_aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
}
}
/**
* Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.<BR>
* <BR>
* @param target The L2Character to attack
*/
@Override
protected void onIntentionAttack(L2Character target)
{
// Calculate the attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
// Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
// if (_actor.getTarget() != null)
super.onIntentionAttack(target);
}
/**
* Manage AI standard thinks of a L2Attackable (called by onEvtThink).<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Update every 1s the _globalAggro counter to come close to 0</li>
* <li>If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it</li>
* <li>If the actor can't attack, order to it to return to its home location</li>
*/
private void thinkActive()
{
L2Attackable npc = (L2Attackable) _actor;
// Update every 1s the _globalAggro counter to come close to 0
if (_globalAggro != 0)
{
if (_globalAggro < 0)
{
_globalAggro++;
}
else
{
_globalAggro--;
}
}
// Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
// A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
if (_globalAggro >= 0)
{
for (L2Character target : npc.getKnownList().getKnownCharactersInRadius(_attackRange))
{
if (target == null)
{
continue;
}
if (autoAttackCondition(target)) // check aggression
{
// Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
final int hating = npc.getHating(target);
// Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
if (hating == 0)
{
npc.addDamageHate(target, 0, 1);
}
}
}
// Chose a target from its aggroList
L2Character hated;
// Force mobs to attak anybody if confused
if (_actor.isConfused())
{
hated = getAttackTarget();
}
else
{
hated = npc.getMostHated();
}
// Order to the L2Attackable to attack the target
if (hated != null)
{
// Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
final int aggro = npc.getHating(hated);
if ((aggro + _globalAggro) > 0)
{
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
// Set the AI Intention to AI_INTENTION_ATTACK
setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
}
return;
}
}
// Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack
((L2SiegeGuardInstance) _actor).returnHome();
return;
}
private void attackPrepare()
{
// Get all information needed to chose between physical or magical attack
L2Skill[] skills = null;
double dist_2 = 0;
int range = 0;
L2SiegeGuardInstance sGuard = (L2SiegeGuardInstance) _actor;
final L2Character attackTarget = getAttackTarget();
try
{
_actor.setTarget(attackTarget);
skills = _actor.getAllSkills();
dist_2 = _actor.getPlanDistanceSq(attackTarget.getX(), attackTarget.getY());
range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + attackTarget.getTemplate().collisionRadius;
}
catch (NullPointerException e)
{
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
// never attack defenders
if ((attackTarget instanceof L2PcInstance) && sGuard.getCastle().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan()))
{
// Cancel the target
sGuard.stopHating(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
if (!GeoData.getInstance().canSeeTarget(_actor, attackTarget))
{
// Siege guards differ from normal mobs currently:
// If target cannot seen, don't attack any more
sGuard.stopHating(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
return;
}
// Check if the actor isn't muted and if it is far from target
if (!_actor.isMuted() && (dist_2 > ((range + 20) * (range + 20))))
{
// check for long ranged skills and heal/buff skills
if (!Config.ALT_GAME_MOB_ATTACK_AI || ((_actor instanceof L2MonsterInstance) && (Rnd.nextInt(100) <= 5)))
{
for (L2Skill sk : skills)
{
final int castRange = sk.getCastRange();
if (((sk.getSkillType() == L2Skill.SkillType.BUFF) || (sk.getSkillType() == L2Skill.SkillType.HEAL) || ((dist_2 >= ((castRange * castRange) / 9)) && (dist_2 <= (castRange * castRange)) && (castRange > 70))) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
{
if ((sk.getSkillType() == L2Skill.SkillType.BUFF) || (sk.getSkillType() == L2Skill.SkillType.HEAL))
{
boolean useSkillSelf = true;
if (((sk.getSkillType() == L2Skill.SkillType.BUFF) || (sk.getSkillType() == L2Skill.SkillType.HEAL) || ((dist_2 >= ((castRange * castRange) / 9)) && (dist_2 <= (castRange * castRange)) && (castRange > 70))) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
{
useSkillSelf = false;
break;
}
if (sk.getSkillType() == L2Skill.SkillType.BUFF)
{
L2Effect[] effects = _actor.getAllEffects();
for (int i = 0; (effects != null) && (i < effects.length); i++)
{
final L2Effect effect = effects[i];
if (effect.getSkill() == sk)
{
useSkillSelf = false;
break;
}
}
}
if (useSkillSelf)
{
_actor.setTarget(_actor);
}
}
L2Object OldTarget = _actor.getTarget();
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
// Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
if (!_actor.isAttackingNow() && (_actor.getRunSpeed() == 0) && _actor.getKnownList().knowsObject(attackTarget))
{
// Cancel the target
_actor.getKnownList().removeKnownObject(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
}
else
{
final double dx = _actor.getX() - attackTarget.getX();
final double dy = _actor.getY() - attackTarget.getY();
final double dz = _actor.getZ() - attackTarget.getZ();
final double homeX = attackTarget.getX() - sGuard.getHomeX();
final double homeY = attackTarget.getY() - sGuard.getHomeY();
// Check if the L2SiegeGuardInstance isn't too far from it's home location
if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) && _actor.getKnownList().knowsObject(attackTarget))
{
// Cancel the target
_actor.getKnownList().removeKnownObject(attackTarget);
_actor.setTarget(null);
setIntention(AI_INTENTION_IDLE, null, null);
}
else // Temporary hack for preventing guards jumping off towers,
// before replacing this with effective geodata checks and AI modification
if ((dz * dz) < (170 * 170))
{
moveToPawn(attackTarget, range);
}
}
return;
}
// Else, if the actor is muted and far from target, just "move to pawn"
else if (_actor.isMuted() && (dist_2 > ((range + 20) * (range + 20))))
{
// Temporary hack for preventing guards jumping off towers,
// before replacing this with effective geodata checks and AI modification
final double dz = _actor.getZ() - attackTarget.getZ();
// normally 130 if guard z coordinates correct
if ((dz * dz) < (170 * 170))
{
moveToPawn(attackTarget, range);
}
return;
}
// Else, if this is close enough to attack
else if (dist_2 <= ((range + 20) * (range + 20)))
{
// Force mobs to attak anybody if confused
L2Character hated = null;
if (_actor.isConfused())
{
hated = attackTarget;
}
else
{
hated = ((L2Attackable) _actor).getMostHated();
}
if (hated == null)
{
setIntention(AI_INTENTION_ACTIVE, null, null);
return;
}
if (hated != attackTarget)
{
setAttackTarget(hated);
}
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
// check for close combat skills && heal/buff skills
if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
{
for (L2Skill sk : skills)
{
final int castRange = sk.getCastRange();
if (((castRange * castRange) >= dist_2) && (castRange <= 70) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
{
if ((sk.getSkillType() == L2Skill.SkillType.BUFF) || (sk.getSkillType() == L2Skill.SkillType.HEAL))
{
boolean useSkillSelf = true;
if ((sk.getSkillType() == L2Skill.SkillType.HEAL) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
{
useSkillSelf = false;
break;
}
if (sk.getSkillType() == L2Skill.SkillType.BUFF)
{
final L2Effect[] effects = _actor.getAllEffects();
for (int i = 0; (effects != null) && (i < effects.length); i++)
{
final L2Effect effect = effects[i];
if (effect.getSkill() == sk)
{
useSkillSelf = false;
break;
}
}
}
if (useSkillSelf)
{
_actor.setTarget(_actor);
}
}
L2Object OldTarget = _actor.getTarget();
clientStopMoving(null);
_accessor.doCast(sk);
_actor.setTarget(OldTarget);
return;
}
}
}
// Finally, do the physical attack itself
_accessor.doAttack(getAttackTarget());
}
}
/**
* Manage AI attack thinks of a L2Attackable (called by onEvtThink).<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Update the attack timeout if actor is running</li>
* <li>If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE</li>
* <li>Call all L2Object of its Faction inside the Faction Range</li>
* <li>Chose a target and order to attack it with magic skill or physical attack</li><BR>
* <BR>
* TODO: Manage casting rules to healer mobs (like Ant Nurses)
*/
private void thinkAttack()
{
if (Config.DEBUG)
{
LOGGER.info("L2SiegeGuardAI.thinkAttack(); timeout=" + (_attackTimeout - GameTimeController.getGameTicks()));
}
if (_attackTimeout < GameTimeController.getGameTicks())
{
// Check if the actor is running
if (_actor.isRunning())
{
// Set the actor movement type to walk and send Server->Client packet ChangeMoveType to all others L2PcInstance
_actor.setWalking();
// Calculate a new attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
}
}
final L2Character attackTarget = getAttackTarget();
// Check if target is dead or if timeout is expired to stop this attack
if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getGameTicks()))
{
// Stop hating this target after the attack timeout or if target is dead
if (attackTarget != null)
{
L2Attackable npc = (L2Attackable) _actor;
npc.stopHating(attackTarget);
}
// Cancel target and timeout
_attackTimeout = Integer.MAX_VALUE;
setAttackTarget(null);
// Set the AI Intention to AI_INTENTION_ACTIVE
setIntention(AI_INTENTION_ACTIVE, null, null);
_actor.setWalking();
return;
}
attackPrepare();
factionNotify();
}
private final void factionNotify()
{
final L2Character actor = getActor();
final L2Character target = getAttackTarget();
// Call all L2Object of its Faction inside the Faction Range
if ((actor == null) || (target == null) || (((L2NpcInstance) actor).getFactionId() == null))
{
return;
}
if (target.isInvul())
{
return;
}
// Go through all L2Object that belong to its faction
for (L2Character cha : actor.getKnownList().getKnownCharactersInRadius(1000))
{
if (cha == null)
{
continue;
}
if (!(cha instanceof L2NpcInstance))
{
continue;
}
L2NpcInstance npc = (L2NpcInstance) cha;
String faction_id = ((L2NpcInstance) actor).getFactionId();
if (!faction_id.equalsIgnoreCase(npc.getFactionId()))
{
continue;
}
// Check if the L2Object is inside the Faction Range of the actor
if ((npc.getAI() != null) && ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE)) && actor.isInsideRadius(npc, npc.getFactionRange(), false, true) && target.isInsideRadius(npc, npc.getFactionRange(), false, true))
{
if (Config.PATHFINDING > 0)
{
if (GeoData.getInstance().canSeeTarget(npc, target))
{
// Notify the L2Object AI with EVT_AGGRESSION
final L2CharacterAI ai = npc.getAI();
if (ai != null)
{
ai.notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
}
}
}
else if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600))
{
// Notify the L2Object AI with EVT_AGGRESSION
final L2CharacterAI ai = npc.getAI();
if (ai != null)
{
ai.notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
}
}
}
}
}
/**
* Manage AI thinking actions of a L2Attackable.<BR>
* <BR>
*/
@Override
protected void onEvtThink()
{
// Check if the actor can't use skills and if a thinking action isn't already in progress
if (_thinking || _actor.isAllSkillsDisabled())
{
return;
}
// Start thinking action
_thinking = true;
try
{
// Manage AI thinks of a L2Attackable
if (getIntention() == AI_INTENTION_ACTIVE)
{
thinkActive();
}
else if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
}
finally
{
// Stop thinking action
_thinking = false;
}
}
/**
* Launch actions corresponding to the Event Attacked.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList</li>
* <li>Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance</li>
* <li>Set the Intention to AI_INTENTION_ATTACK</li> <BR>
* <BR>
* @param attacker The L2Character that attacks the actor
*/
@Override
protected void onEvtAttacked(L2Character attacker)
{
// Calculate the attack timeout
_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
// Set the _globalAggro to 0 to permit attack even just after spawn
if (_globalAggro < 0)
{
_globalAggro = 0;
}
// Add the attacker to the _aggroList of the actor
((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
// Set the Intention to AI_INTENTION_ATTACK
if (getIntention() != AI_INTENTION_ATTACK)
{
setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
}
super.onEvtAttacked(attacker);
}
/**
* Launch actions corresponding to the Event Aggression.<BR>
* <BR>
* <B><U> Actions</U> :</B><BR>
* <BR>
* <li>Add the target to the actor _aggroList or update hate if already present</li>
* <li>Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)</li><BR>
* <BR>
* @param target The L2Character that attacks
* @param aggro The value of hate to add to the actor against the target
*/
@Override
protected void onEvtAggression(L2Character target, int aggro)
{
if (_actor == null)
{
return;
}
L2Attackable me = (L2Attackable) _actor;
if (target != null)
{
// Add the target to the actor _aggroList or update hate if already present
me.addDamageHate(target, 0, aggro);
// Get the hate of the actor against the target
aggro = me.getHating(target);
if (aggro <= 0)
{
if (me.getMostHated() == null)
{
_globalAggro = -25;
me.clearAggroList();
setIntention(AI_INTENTION_IDLE, null, null);
}
return;
}
// Set the actor AI Intention to AI_INTENTION_ATTACK
if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
{
// Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
if (!_actor.isRunning())
{
_actor.setRunning();
}
L2SiegeGuardInstance sGuard = (L2SiegeGuardInstance) _actor;
final double homeX = target.getX() - sGuard.getHomeX();
final double homeY = target.getY() - sGuard.getHomeY();
// Check if the L2SiegeGuardInstance is not too far from its home location
if (((homeX * homeX) + (homeY * homeY)) < 3240000)
{
setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
}
}
}
else
{
// currently only for setting lower general aggro
if (aggro >= 0)
{
return;
}
L2Character mostHated = me.getMostHated();
if (mostHated == null)
{
_globalAggro = -25;
return;
}
for (L2Character aggroed : me.getAggroListRP().keySet())
{
me.addDamageHate(aggroed, 0, aggro);
}
aggro = me.getHating(mostHated);
if (aggro <= 0)
{
_globalAggro = -25;
me.clearAggroList();
setIntention(AI_INTENTION_IDLE, null, null);
}
}
}
@Override
protected void onEvtDead()
{
stopAITask();
super.onEvtDead();
}
public void stopAITask()
{
if (_aiTask != null)
{
_aiTask.cancel(false);
_aiTask = null;
}
_accessor.detachAI();
}
}

View File

@ -0,0 +1,223 @@
/*
* 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.gameserver.ai;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_CAST;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_INTERACT;
import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_PICK_UP;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Character.AIAccessor;
import com.l2jmobius.gameserver.model.actor.L2Summon;
public class L2SummonAI extends L2CharacterAI
{
private boolean _thinking; // to prevent recursive thinking
private L2Summon summon;
public L2SummonAI(AIAccessor accessor)
{
super(accessor);
}
@Override
protected void onIntentionIdle()
{
stopFollow();
onIntentionActive();
}
@Override
protected void onIntentionActive()
{
L2Summon summon = (L2Summon) _actor;
if (summon.getFollowStatus())
{
setIntention(AI_INTENTION_FOLLOW, summon.getOwner());
}
else
{
super.onIntentionActive();
}
}
private void thinkAttack()
{
summon = (L2Summon) _actor;
L2Object target = null;
target = summon.getTarget();
// Like L2OFF if the target is dead the summon must go back to his owner
if ((target != null) && (summon != null) && ((L2Character) target).isDead())
{
summon.setFollowStatus(true);
}
if (checkTargetLostOrDead(getAttackTarget()))
{
setAttackTarget(null);
return;
}
if (maybeMoveToPawn(getAttackTarget(), _actor.getPhysicalAttackRange()))
{
return;
}
clientStopMoving(null);
_accessor.doAttack(getAttackTarget());
return;
}
private void thinkCast()
{
L2Summon summon = (L2Summon) _actor;
final L2Character target = getCastTarget();
if (checkTargetLost(target))
{
setCastTarget(null);
return;
}
final L2Skill skill = get_skill();
if (maybeMoveToPawn(target, _actor.getMagicalAttackRange(skill)))
{
return;
}
clientStopMoving(null);
summon.setFollowStatus(false);
setIntention(AI_INTENTION_IDLE);
_accessor.doCast(skill);
return;
}
private void thinkPickUp()
{
if (_actor.isAllSkillsDisabled())
{
return;
}
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
((L2Summon.AIAccessor) _accessor).doPickupItem(target);
return;
}
private void thinkInteract()
{
if (_actor.isAllSkillsDisabled())
{
return;
}
final L2Object target = getTarget();
if (checkTargetLost(target))
{
return;
}
if (maybeMoveToPawn(target, 36))
{
return;
}
setIntention(AI_INTENTION_IDLE);
return;
}
@Override
protected void onEvtThink()
{
if (_thinking || _actor.isAllSkillsDisabled())
{
return;
}
_thinking = true;
try
{
if (getIntention() == AI_INTENTION_ATTACK)
{
thinkAttack();
}
else if (getIntention() == AI_INTENTION_CAST)
{
thinkCast();
}
else if (getIntention() == AI_INTENTION_PICK_UP)
{
thinkPickUp();
}
else if (getIntention() == AI_INTENTION_INTERACT)
{
thinkInteract();
}
}
finally
{
_thinking = false;
}
}
@Override
protected void onEvtFinishCasting()
{
super.onEvtFinishCasting();
final L2Summon summon = (L2Summon) _actor;
L2Object target = null;
target = summon.getTarget();
if (target == null)
{
return;
}
if (summon.getAI().getIntention() != AI_INTENTION_ATTACK)
{
summon.setFollowStatus(true);
}
else if (((L2Character) target).isDead())
{
summon.setFollowStatus(true);
}
}
}

View File

@ -0,0 +1,397 @@
/*
* 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.gameserver.cache;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.datatables.sql.ClanTable;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.model.L2Clan;
/**
* @author Layane
*/
public class CrestCache
{
private static Logger LOGGER = Logger.getLogger(CrestCache.class.getName());
private static CrestCache _instance;
private final Map<Integer, byte[]> _cachePledge = new HashMap<>();
private final Map<Integer, byte[]> _cachePledgeLarge = new HashMap<>();
private final Map<Integer, byte[]> _cacheAlly = new HashMap<>();
private int _loadedFiles;
private long _bytesBuffLen;
public static CrestCache getInstance()
{
if (_instance == null)
{
_instance = new CrestCache();
}
return _instance;
}
public CrestCache()
{
convertOldPedgeFiles();
reload();
}
public void reload()
{
final FileFilter filter = new BmpFilter();
final File dir = new File(Config.DATAPACK_ROOT, "data/crests/");
final File[] files = dir.listFiles(filter);
byte[] content;
synchronized (this)
{
_loadedFiles = 0;
_bytesBuffLen = 0;
_cachePledge.clear();
_cachePledgeLarge.clear();
_cacheAlly.clear();
}
for (File file : files)
{
RandomAccessFile f = null;
synchronized (this)
{
try
{
f = new RandomAccessFile(file, "r");
content = new byte[(int) f.length()];
f.readFully(content);
if (file.getName().startsWith("Crest_Large_"))
{
_cachePledgeLarge.put(Integer.valueOf(file.getName().substring(12, file.getName().length() - 4)), content);
}
else if (file.getName().startsWith("Crest_"))
{
_cachePledge.put(Integer.valueOf(file.getName().substring(6, file.getName().length() - 4)), content);
}
else if (file.getName().startsWith("AllyCrest_"))
{
_cacheAlly.put(Integer.valueOf(file.getName().substring(10, file.getName().length() - 4)), content);
}
_loadedFiles++;
_bytesBuffLen += content.length;
}
catch (Exception e)
{
LOGGER.warning("problem with crest bmp file " + e);
}
finally
{
if (f != null)
{
try
{
f.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
}
LOGGER.info("Cache[Crest]: " + String.format("%.3f", getMemoryUsage()) + "MB on " + getLoadedFiles() + " files loaded.");
}
public void convertOldPedgeFiles()
{
File dir = new File(Config.DATAPACK_ROOT, "data/crests/");
File[] files = dir.listFiles(new OldPledgeFilter());
if (files == null)
{
LOGGER.info("No old crest files found in \"data/crests/\"!!! May be you deleted them?");
return;
}
for (File file : files)
{
final int clanId = Integer.parseInt(file.getName().substring(7, file.getName().length() - 4));
LOGGER.info("Found old crest file \"" + file.getName() + "\" for clanId " + clanId);
final int newId = IdFactory.getInstance().getNextId();
L2Clan clan = ClanTable.getInstance().getClan(clanId);
if (clan != null)
{
removeOldPledgeCrest(clan.getCrestId());
file.renameTo(new File(Config.DATAPACK_ROOT, "data/crests/Crest_" + newId + ".bmp"));
LOGGER.info("Renamed Clan crest to new format: Crest_" + newId + ".bmp");
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("UPDATE clan_data SET crest_id = ? WHERE clan_id = ?");
statement.setInt(1, newId);
statement.setInt(2, clan.getClanId());
statement.executeUpdate();
statement.close();
}
catch (SQLException e)
{
LOGGER.warning("could not update the crest id:" + e.getMessage());
}
clan.setCrestId(newId);
clan.setHasCrest(true);
}
else
{
LOGGER.info("Clan Id: " + clanId + " does not exist in table.. deleting.");
file.delete();
}
}
}
public float getMemoryUsage()
{
return (float) _bytesBuffLen / 1048576;
}
public int getLoadedFiles()
{
return _loadedFiles;
}
public byte[] getPledgeCrest(int id)
{
return _cachePledge.get(id);
}
public byte[] getPledgeCrestLarge(int id)
{
return _cachePledgeLarge.get(id);
}
public byte[] getAllyCrest(int id)
{
return _cacheAlly.get(id);
}
public void removePledgeCrest(int id)
{
File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Crest_" + id + ".bmp");
_cachePledge.remove(id);
try
{
crestFile.delete();
}
catch (Exception e)
{
}
}
public void removePledgeCrestLarge(int id)
{
File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Crest_Large_" + id + ".bmp");
_cachePledgeLarge.remove(id);
try
{
crestFile.delete();
}
catch (Exception e)
{
}
}
public void removeOldPledgeCrest(int id)
{
File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Pledge_" + id + ".bmp");
try
{
crestFile.delete();
}
catch (Exception e)
{
}
}
public void removeAllyCrest(int id)
{
File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/AllyCrest_" + id + ".bmp");
_cacheAlly.remove(id);
try
{
crestFile.delete();
}
catch (Exception e)
{
}
}
public boolean savePledgeCrest(int newId, byte[] data)
{
boolean output = false;
final File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Crest_" + newId + ".bmp");
FileOutputStream out = null;
try
{
out = new FileOutputStream(crestFile);
out.write(data);
_cachePledge.put(newId, data);
output = true;
}
catch (IOException e)
{
LOGGER.warning("Error saving pledge crest" + crestFile + " " + e);
}
finally
{
if (out != null)
{
try
{
out.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return output;
}
public boolean savePledgeCrestLarge(int newId, byte[] data)
{
boolean output = false;
final File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/Crest_Large_" + newId + ".bmp");
FileOutputStream out = null;
try
{
out = new FileOutputStream(crestFile);
out.write(data);
_cachePledgeLarge.put(newId, data);
output = true;
}
catch (IOException e)
{
LOGGER.warning("Error saving Large pledge crest" + crestFile + " " + e);
}
finally
{
if (out != null)
{
try
{
out.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return output;
}
public boolean saveAllyCrest(int newId, byte[] data)
{
boolean output = false;
final File crestFile = new File(Config.DATAPACK_ROOT, "data/crests/AllyCrest_" + newId + ".bmp");
FileOutputStream out = null;
try
{
out = new FileOutputStream(crestFile);
out.write(data);
_cacheAlly.put(newId, data);
output = true;
}
catch (IOException e)
{
LOGGER.warning("Error saving ally crest" + crestFile + " " + e);
}
finally
{
if (out != null)
{
try
{
out.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return output;
}
class BmpFilter implements FileFilter
{
@Override
public boolean accept(File file)
{
return file.getName().endsWith(".bmp");
}
}
class OldPledgeFilter implements FileFilter
{
@Override
public boolean accept(File file)
{
return file.getName().startsWith("Pledge_");
}
}
}

View File

@ -0,0 +1,251 @@
/*
* 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.gameserver.cache;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.util.Util;
/**
* @author Layane
*/
public class HtmCache
{
private static Logger LOGGER = Logger.getLogger(HtmCache.class.getName());
private static HtmCache _instance;
private final Map<Integer, String> _cache;
private int _loadedFiles;
private long _bytesBuffLen;
public static HtmCache getInstance()
{
if (_instance == null)
{
_instance = new HtmCache();
}
return _instance;
}
public HtmCache()
{
_cache = new HashMap<>();
reload();
}
public void reload()
{
reload(Config.DATAPACK_ROOT);
}
public void reload(File f)
{
if (!Config.LAZY_CACHE)
{
LOGGER.info("Html cache start...");
parseDir(f);
LOGGER.info("Cache[HTML]: " + String.format("%.3f", getMemoryUsage()) + " megabytes on " + getLoadedFiles() + " files loaded");
}
else
{
_cache.clear();
_loadedFiles = 0;
_bytesBuffLen = 0;
LOGGER.info("Cache[HTML]: Running lazy cache");
}
}
public void reloadPath(File f)
{
parseDir(f);
LOGGER.info("Cache[HTML]: Reloaded specified path.");
}
public double getMemoryUsage()
{
return (float) _bytesBuffLen / 1048576;
}
public int getLoadedFiles()
{
return _loadedFiles;
}
class HtmFilter implements FileFilter
{
@Override
public boolean accept(File file)
{
if (!file.isDirectory())
{
return file.getName().endsWith(".htm") || file.getName().endsWith(".html");
}
return true;
}
}
private void parseDir(File dir)
{
FileFilter filter = new HtmFilter();
File[] files = dir.listFiles(filter);
for (File file : files)
{
if (!file.isDirectory())
{
loadFile(file);
}
else
{
parseDir(file);
}
}
}
public String loadFile(File file)
{
final HtmFilter filter = new HtmFilter();
String content = null;
if (file.exists() && filter.accept(file) && !file.isDirectory())
{
FileInputStream fis = null;
BufferedInputStream bis = null;
try
{
fis = new FileInputStream(file);
bis = new BufferedInputStream(fis);
final int bytes = bis.available();
final byte[] raw = new byte[bytes];
bis.read(raw);
content = new String(raw, "UTF-8");
content = content.replaceAll("\r\n", "\n");
content = content.replaceAll("(?s)<!--.*?-->", ""); // Remove html comments
final String relpath = Util.getRelativePath(Config.DATAPACK_ROOT, file);
final int hashcode = relpath.hashCode();
final String oldContent = _cache.get(hashcode);
if (oldContent == null)
{
_bytesBuffLen += bytes;
_loadedFiles++;
}
else
{
_bytesBuffLen = (_bytesBuffLen - oldContent.length()) + bytes;
}
_cache.put(hashcode, content);
}
catch (Exception e)
{
LOGGER.warning("problem with htm file " + e);
e.printStackTrace();
}
finally
{
if (bis != null)
{
try
{
bis.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (fis != null)
{
try
{
fis.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
return content;
}
public String getHtmForce(String path)
{
String content = getHtm(path);
if (content == null)
{
content = "<html><body>My text is missing:<br>" + path + "</body></html>";
LOGGER.warning("Cache[HTML]: Missing HTML page: " + path);
}
return content;
}
public String getHtm(String path)
{
String content = _cache.get(path.hashCode());
if (Config.LAZY_CACHE && (content == null))
{
content = loadFile(new File(Config.DATAPACK_ROOT, path));
}
return content;
}
public boolean contains(String path)
{
return _cache.containsKey(path.hashCode());
}
/**
* Check if an HTM exists and can be loaded
* @param path The path to the HTM
* @return
*/
public boolean isLoadable(String path)
{
File file = new File(path);
HtmFilter filter = new HtmFilter();
if (file.exists() && filter.accept(file) && !file.isDirectory())
{
return true;
}
return false;
}
}

View File

@ -0,0 +1,78 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.cache;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.l2jmobius.Config;
import com.l2jmobius.commons.concurrent.ThreadPoolManager;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
/**
* @author -Nemesiss-
*/
public class WarehouseCacheManager
{
private static WarehouseCacheManager _instance;
protected final Map<L2PcInstance, Long> _cachedWh;
protected final long _cacheTime;
public static WarehouseCacheManager getInstance()
{
if (_instance == null)
{
_instance = new WarehouseCacheManager();
}
return _instance;
}
private WarehouseCacheManager()
{
_cacheTime = Config.WAREHOUSE_CACHE_TIME * 60000L; // 60*1000 = 60000
_cachedWh = new ConcurrentHashMap<>();
ThreadPoolManager.scheduleAtFixedRate(new CacheScheduler(), 120000, 60000);
}
public void addCacheTask(L2PcInstance pc)
{
_cachedWh.put(pc, System.currentTimeMillis());
}
public void remCacheTask(L2PcInstance pc)
{
_cachedWh.remove(pc);
}
public class CacheScheduler implements Runnable
{
@Override
public void run()
{
final long cTime = System.currentTimeMillis();
for (L2PcInstance pc : _cachedWh.keySet())
{
if ((cTime - _cachedWh.get(pc)) > _cacheTime)
{
pc.clearWarehouse();
_cachedWh.remove(pc);
}
}
}
}
}

View File

@ -0,0 +1,257 @@
/*
* 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.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.Manager.ForumsBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.TopicBBSManager;
public class Forum
{
// Types
public static final int ROOT = 0;
public static final int NORMAL = 1;
public static final int CLAN = 2;
public static final int MEMO = 3;
public static final int MAIL = 4;
// Permissions
public static final int INVISIBLE = 0;
public static final int ALL = 1;
public static final int CLANMEMBERONLY = 2;
public static final int OWNERONLY = 3;
private static Logger LOGGER = Logger.getLogger(Forum.class.getName());
private final List<Forum> _children;
private final Map<Integer, Topic> _topic;
private final int _forumId;
private String _forumName;
private int _forumType;
private int _forumPost;
private int _forumPerm;
private final Forum _fParent;
private int _ownerID;
private boolean _loaded = false;
/**
* @param Forumid
* @param FParent
*/
public Forum(int Forumid, Forum FParent)
{
_forumId = Forumid;
_fParent = FParent;
_children = new ArrayList<>();
_topic = new HashMap<>();
}
/**
* @param name
* @param parent
* @param type
* @param perm
* @param OwnerID
*/
public Forum(String name, Forum parent, int type, int perm, int OwnerID)
{
_forumName = name;
_forumId = ForumsBBSManager.getInstance().getANewID();
_forumType = type;
_forumPost = 0;
_forumPerm = perm;
_fParent = parent;
_ownerID = OwnerID;
_children = new ArrayList<>();
_topic = new HashMap<>();
parent._children.add(this);
ForumsBBSManager.getInstance().addForum(this);
_loaded = true;
}
private void load()
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM forums WHERE forum_id=?");
statement.setInt(1, _forumId);
ResultSet result = statement.executeQuery();
if (result.next())
{
_forumName = result.getString("forum_name");
_forumPost = result.getInt("forum_post");
_forumType = result.getInt("forum_type");
_forumPerm = result.getInt("forum_perm");
_ownerID = result.getInt("forum_owner_id");
}
result.close();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Data error on Forum " + _forumId + " : " + e);
}
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM topic WHERE topic_forum_id=? ORDER BY topic_id DESC");
statement.setInt(1, _forumId);
ResultSet result = statement.executeQuery();
while (result.next())
{
Topic t = new Topic(Topic.ConstructorType.RESTORE, result.getInt("topic_id"), result.getInt("topic_forum_id"), result.getString("topic_name"), result.getLong("topic_date"), result.getString("topic_ownername"), result.getInt("topic_ownerid"), result.getInt("topic_type"), result.getInt("topic_reply"));
_topic.put(t.getID(), t);
if (t.getID() > TopicBBSManager.getInstance().getMaxID(this))
{
TopicBBSManager.getInstance().setMaxID(t.getID(), this);
}
}
result.close();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Data error on Forum " + _forumId + " : " + e);
}
}
private void getChildren()
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT forum_id FROM forums WHERE forum_parent=?");
statement.setInt(1, _forumId);
ResultSet result = statement.executeQuery();
while (result.next())
{
final Forum f = new Forum(result.getInt("forum_id"), this);
_children.add(f);
ForumsBBSManager.getInstance().addForum(f);
}
result.close();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Data error on Forum (children): " + e);
}
}
public int getTopicSize()
{
vload();
return _topic.size();
}
public Topic getTopic(int j)
{
vload();
return _topic.get(j);
}
public void addTopic(Topic t)
{
vload();
_topic.put(t.getID(), t);
}
public int getID()
{
return _forumId;
}
public String getName()
{
vload();
return _forumName;
}
public int getType()
{
vload();
return _forumType;
}
/**
* @param name
* @return
*/
public Forum getChildByName(String name)
{
vload();
for (Forum f : _children)
{
if (f.getName().equals(name))
{
return f;
}
}
return null;
}
public void rmTopicByID(int id)
{
_topic.remove(id);
}
public void insertIntoDb()
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("INSERT INTO forums (forum_id,forum_name,forum_parent,forum_post,forum_type,forum_perm,forum_owner_id) values (?,?,?,?,?,?,?)");
statement.setInt(1, _forumId);
statement.setString(2, _forumName);
statement.setInt(3, _fParent.getID());
statement.setInt(4, _forumPost);
statement.setInt(5, _forumType);
statement.setInt(6, _forumPerm);
statement.setInt(7, _ownerID);
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Error while saving new Forum to db " + e);
}
}
public void vload()
{
if (!_loaded)
{
load();
getChildren();
_loaded = true;
}
}
}

View File

@ -0,0 +1,176 @@
/*
* 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.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.Manager.PostBBSManager;
/**
* @author Maktakien
*/
public class Post
{
private static Logger LOGGER = Logger.getLogger(Post.class.getName());
public class CPost
{
public int postId;
public String postOwner;
public int postOwnerId;
public long postDate;
public int postTopicId;
public int postForumId;
public String postTxt;
}
private final List<CPost> _post;
/**
* @param _PostOwner
* @param _PostOwnerID
* @param date
* @param tid
* @param _PostForumID
* @param txt
*/
public Post(String _PostOwner, int _PostOwnerID, long date, int tid, int _PostForumID, String txt)
{
_post = new ArrayList<>();
CPost cp = new CPost();
cp.postId = 0;
cp.postOwner = _PostOwner;
cp.postOwnerId = _PostOwnerID;
cp.postDate = date;
cp.postTopicId = tid;
cp.postForumId = _PostForumID;
cp.postTxt = txt;
_post.add(cp);
insertindb(cp);
}
public void insertindb(CPost cp)
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("INSERT INTO posts (post_id,post_owner_name,post_ownerid,post_date,post_topic_id,post_forum_id,post_txt) values (?,?,?,?,?,?,?)");
statement.setInt(1, cp.postId);
statement.setString(2, cp.postOwner);
statement.setInt(3, cp.postOwnerId);
statement.setLong(4, cp.postDate);
statement.setInt(5, cp.postTopicId);
statement.setInt(6, cp.postForumId);
statement.setString(7, cp.postTxt);
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Error while saving new Post to db " + e);
}
}
public Post(Topic t)
{
_post = new ArrayList<>();
load(t);
}
public CPost getCPost(int id)
{
int i = 0;
for (CPost cp : _post)
{
if (i++ == id)
{
return cp;
}
}
return null;
}
public void deleteMe(Topic t)
{
PostBBSManager.getInstance().delPostByTopic(t);
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("DELETE FROM posts WHERE post_forum_id=? AND post_topic_id=?");
statement.setInt(1, t.getForumID());
statement.setInt(2, t.getID());
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Error while deleting post: " + e.getMessage());
}
}
private void load(Topic t)
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT * FROM posts WHERE post_forum_id=? AND post_topic_id=? ORDER BY post_id ASC");
statement.setInt(1, t.getForumID());
statement.setInt(2, t.getID());
ResultSet result = statement.executeQuery();
while (result.next())
{
CPost cp = new CPost();
cp.postId = result.getInt("post_id");
cp.postOwner = result.getString("post_owner_name");
cp.postOwnerId = result.getInt("post_ownerid");
cp.postDate = result.getLong("post_date");
cp.postTopicId = result.getInt("post_topic_id");
cp.postForumId = result.getInt("post_forum_id");
cp.postTxt = result.getString("post_txt");
_post.add(cp);
}
result.close();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Data error on Post " + t.getForumID() + "/" + t.getID() + " : " + e);
}
}
public void updateText(int i)
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
CPost cp = getCPost(i);
PreparedStatement statement = con.prepareStatement("UPDATE posts SET post_txt=? WHERE post_id=? AND post_topic_id=? AND post_forum_id=?");
statement.setString(1, cp.postTxt);
statement.setInt(2, cp.postId);
statement.setInt(3, cp.postTopicId);
statement.setInt(4, cp.postForumId);
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Error while saving new Post to db " + e);
}
}
}

View File

@ -0,0 +1,141 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.BB;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.logging.Logger;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.Manager.TopicBBSManager;
public class Topic
{
private static Logger LOGGER = Logger.getLogger(Topic.class.getName());
public static final int MORMAL = 0;
public static final int MEMO = 1;
private final int _id;
private final int _forumId;
private final String _topicName;
private final long _date;
private final String _ownerName;
private final int _ownerId;
private final int _type;
private final int _cReply;
/**
* @param ct
* @param id
* @param fid
* @param name
* @param date
* @param oname
* @param oid
* @param type
* @param Creply
*/
public Topic(ConstructorType ct, int id, int fid, String name, long date, String oname, int oid, int type, int Creply)
{
_id = id;
_forumId = fid;
_topicName = name;
_date = date;
_ownerName = oname;
_ownerId = oid;
_type = type;
_cReply = Creply;
TopicBBSManager.getInstance().addTopic(this);
if (ct == ConstructorType.CREATE)
{
insertIntoDb();
}
}
private void insertIntoDb()
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("INSERT INTO topic (topic_id,topic_forum_id,topic_name,topic_date,topic_ownername,topic_ownerid,topic_type,topic_reply) values (?,?,?,?,?,?,?,?)");
statement.setInt(1, _id);
statement.setInt(2, _forumId);
statement.setString(3, _topicName);
statement.setLong(4, _date);
statement.setString(5, _ownerName);
statement.setInt(6, _ownerId);
statement.setInt(7, _type);
statement.setInt(8, _cReply);
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Error while saving new Topic to db " + e);
}
}
public enum ConstructorType
{
RESTORE,
CREATE
}
public int getID()
{
return _id;
}
public int getForumID()
{
return _forumId;
}
public String getName()
{
return _topicName;
}
public String getOwnerName()
{
return _ownerName;
}
public long getDate()
{
return _date;
}
public void deleteMe(Forum f)
{
TopicBBSManager.getInstance().delTopic(this);
f.rmTopicByID(getID());
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("DELETE FROM topic WHERE topic_id=? AND topic_forum_id=?");
statement.setInt(1, getID());
statement.setInt(2, f.getID());
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Error while deleting topic: " + e.getMessage());
}
}
}

View File

@ -0,0 +1,175 @@
/*
* 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.gameserver.communitybbs;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.communitybbs.Manager.BaseBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.ClanBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.FavoriteBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.FriendsBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.MailBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.PostBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.RegionBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.TopBBSManager;
import com.l2jmobius.gameserver.communitybbs.Manager.TopicBBSManager;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.L2GameClient;
import com.l2jmobius.gameserver.network.SystemMessageId;
public class CommunityBoard
{
/** The bypasses used by the players. */
private final Map<Integer, String> _bypasses = new ConcurrentHashMap<>();
protected CommunityBoard()
{
}
public static CommunityBoard getInstance()
{
return SingletonHolder._instance;
}
public void handleCommands(L2GameClient client, String command)
{
final L2PcInstance activeChar = client.getActiveChar();
if (activeChar == null)
{
return;
}
if (!Config.ENABLE_COMMUNITY_BOARD)
{
activeChar.sendPacket(SystemMessageId.CB_OFFLINE);
return;
}
if (command.startsWith("_bbshome"))
{
TopBBSManager.getInstance().parseCmd(command, activeChar);
}
else if (command.startsWith("_bbsloc"))
{
RegionBBSManager.getInstance().parseCmd(command, activeChar);
}
else if (command.startsWith("_bbsclan"))
{
ClanBBSManager.getInstance().parseCmd(command, activeChar);
}
else if (command.startsWith("_bbsmemo"))
{
TopicBBSManager.getInstance().parseCmd(command, activeChar);
}
else if (command.startsWith("_bbsmail") || command.equals("_maillist_0_1_0_"))
{
MailBBSManager.getInstance().parseCmd(command, activeChar);
}
else if (command.startsWith("_friend") || command.startsWith("_block"))
{
FriendsBBSManager.getInstance().parseCmd(command, activeChar);
}
else if (command.startsWith("_bbstopics"))
{
TopicBBSManager.getInstance().parseCmd(command, activeChar);
}
else if (command.startsWith("_bbsposts"))
{
PostBBSManager.getInstance().parseCmd(command, activeChar);
}
else if (command.startsWith("_bbsgetfav") || command.startsWith("bbs_add_fav") || command.startsWith("_bbsdelfav_"))
{
FavoriteBBSManager.getInstance().parseCmd(command, activeChar);
}
else
{
BaseBBSManager.separateAndSend("<html><body><br><br><center>The command: " + command + " isn't implemented.</center></body></html>", activeChar);
}
}
public void handleWriteCommands(L2GameClient client, String url, String arg1, String arg2, String arg3, String arg4, String arg5)
{
final L2PcInstance activeChar = client.getActiveChar();
if (activeChar == null)
{
return;
}
if (!Config.ENABLE_COMMUNITY_BOARD)
{
activeChar.sendPacket(SystemMessageId.CB_OFFLINE);
return;
}
if (url.equals("Topic"))
{
TopicBBSManager.getInstance().parseWrite(arg1, arg2, arg3, arg4, arg5, activeChar);
}
else if (url.equals("Post"))
{
PostBBSManager.getInstance().parseWrite(arg1, arg2, arg3, arg4, arg5, activeChar);
}
else if (url.equals("_bbsloc"))
{
RegionBBSManager.getInstance().parseWrite(arg1, arg2, arg3, arg4, arg5, activeChar);
}
else if (url.equals("_bbsclan"))
{
ClanBBSManager.getInstance().parseWrite(arg1, arg2, arg3, arg4, arg5, activeChar);
}
else if (url.equals("Mail"))
{
MailBBSManager.getInstance().parseWrite(arg1, arg2, arg3, arg4, arg5, activeChar);
}
else if (url.equals("_friend"))
{
FriendsBBSManager.getInstance().parseWrite(arg1, arg2, arg3, arg4, arg5, activeChar);
}
else
{
BaseBBSManager.separateAndSend("<html><body><br><br><center>The command: " + url + " isn't implemented.</center></body></html>", activeChar);
}
}
/**
* Sets the last bypass used by the player.
* @param player the player
* @param title the title
* @param bypass the bypass
*/
public void addBypass(L2PcInstance player, String title, String bypass)
{
_bypasses.put(player.getObjectId(), title + "&" + bypass);
}
/**
* Removes the last bypass used by the player.
* @param player the player
* @return the last bypass used
*/
public String removeBypass(L2PcInstance player)
{
return _bypasses.remove(player.getObjectId());
}
private static class SingletonHolder
{
protected static final CommunityBoard _instance = new CommunityBoard();
}
}

View File

@ -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.gameserver.communitybbs.Manager;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import com.l2jmobius.gameserver.cache.HtmCache;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.serverpackets.ShowBoard;
public abstract class BaseBBSManager
{
protected static final Logger LOGGER = Logger.getLogger(BaseBBSManager.class.getName());
protected static final String CB_PATH = "data/html/CommunityBoard/";
public void parseCmd(String command, L2PcInstance activeChar)
{
separateAndSend("<html><body><br><br><center>The command: " + command + " isn't implemented.</center></body></html>", activeChar);
}
public void parseWrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
separateAndSend("<html><body><br><br><center>The command: " + ar1 + " isn't implemented.</center></body></html>", activeChar);
}
public static void separateAndSend(String html, L2PcInstance acha)
{
if ((html == null) || (acha == null))
{
return;
}
if (html.length() < 4090)
{
acha.sendPacket(new ShowBoard(html, "101"));
acha.sendPacket(ShowBoard.STATIC_SHOWBOARD_102);
acha.sendPacket(ShowBoard.STATIC_SHOWBOARD_103);
}
else if (html.length() < 8180)
{
acha.sendPacket(new ShowBoard(html.substring(0, 4090), "101"));
acha.sendPacket(new ShowBoard(html.substring(4090, html.length()), "102"));
acha.sendPacket(ShowBoard.STATIC_SHOWBOARD_103);
}
else if (html.length() < 12270)
{
acha.sendPacket(new ShowBoard(html.substring(0, 4090), "101"));
acha.sendPacket(new ShowBoard(html.substring(4090, 8180), "102"));
acha.sendPacket(new ShowBoard(html.substring(8180, html.length()), "103"));
}
}
protected static void send1001(String html, L2PcInstance acha)
{
if (html.length() < 8180)
{
acha.sendPacket(new ShowBoard(html, "1001"));
}
}
protected static void send1002(L2PcInstance acha)
{
send1002(acha, " ", " ", "0");
}
protected static void send1002(L2PcInstance activeChar, String string, String string2, String string3)
{
List<String> _arg = new ArrayList<>();
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add("0");
_arg.add(activeChar.getName());
_arg.add(Integer.toString(activeChar.getObjectId()));
_arg.add(activeChar.getAccountName());
_arg.add("9");
_arg.add(string2);
_arg.add(string2);
_arg.add(string);
_arg.add(string3);
_arg.add(string3);
_arg.add("0");
_arg.add("0");
activeChar.sendPacket(new ShowBoard(_arg));
}
/**
* Loads an HTM located in the default CB path.
* @param file : the file to load.
* @param activeChar : the requester.
*/
protected void loadStaticHtm(String file, L2PcInstance activeChar)
{
separateAndSend(HtmCache.getInstance().getHtm(CB_PATH + getFolder() + file), activeChar);
}
/**
* That method is overidden in every board type. It allows to switch of folders following the board.
* @return the folder.
*/
protected String getFolder()
{
return "";
}
}

View File

@ -0,0 +1,359 @@
/*
* 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.gameserver.communitybbs.Manager;
import java.util.StringTokenizer;
import com.l2jmobius.commons.util.StringUtil;
import com.l2jmobius.gameserver.cache.HtmCache;
import com.l2jmobius.gameserver.communitybbs.CommunityBoard;
import com.l2jmobius.gameserver.datatables.sql.ClanTable;
import com.l2jmobius.gameserver.model.L2Clan;
import com.l2jmobius.gameserver.model.L2ClanMember;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.SystemMessageId;
public class ClanBBSManager extends BaseBBSManager
{
protected ClanBBSManager()
{
}
public static ClanBBSManager getInstance()
{
return SingletonHolder._instance;
}
@Override
public void parseCmd(String command, L2PcInstance activeChar)
{
if (command.equalsIgnoreCase("_bbsclan"))
{
if (activeChar.getClan() == null)
{
sendClanList(activeChar, 1);
}
else
{
sendClanDetails(activeChar, activeChar.getClan().getClanId());
}
}
else if (command.startsWith("_bbsclan"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
final String clanCommand = st.nextToken();
if (clanCommand.equalsIgnoreCase("clan"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Clan", command);
sendClanList(activeChar, Integer.parseInt(st.nextToken()));
}
else if (clanCommand.equalsIgnoreCase("home"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Clan Home", command);
sendClanDetails(activeChar, Integer.parseInt(st.nextToken()));
}
else if (clanCommand.equalsIgnoreCase("mail"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Clan Mail", command);
sendClanMail(activeChar, Integer.parseInt(st.nextToken()));
}
else if (clanCommand.equalsIgnoreCase("management"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Clan Management", command);
sendClanManagement(activeChar, Integer.parseInt(st.nextToken()));
}
else if (clanCommand.equalsIgnoreCase("notice"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Clan Notice", command);
if (st.hasMoreTokens())
{
final String noticeCommand = st.nextToken();
if (!noticeCommand.isEmpty() && (activeChar.getClan() != null))
{
activeChar.getClan().setNoticeEnabledAndStore(Boolean.parseBoolean(noticeCommand));
}
}
sendClanNotice(activeChar, activeChar.getClanId());
}
}
else
{
super.parseCmd(command, activeChar);
}
}
@Override
public void parseWrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
if (ar1.equalsIgnoreCase("intro"))
{
if (Integer.valueOf(ar2) != activeChar.getClanId())
{
return;
}
final L2Clan clan = ClanTable.getInstance().getClan(activeChar.getClanId());
if (clan == null)
{
return;
}
clan.setIntroduction(ar3, true);
sendClanManagement(activeChar, Integer.valueOf(ar2));
}
else if (ar1.equals("notice"))
{
activeChar.getClan().setNoticeAndStore(ar4);
sendClanNotice(activeChar, activeChar.getClanId());
}
else if (ar1.equalsIgnoreCase("mail"))
{
if (Integer.valueOf(ar2) != activeChar.getClanId())
{
return;
}
final L2Clan clan = ClanTable.getInstance().getClan(activeChar.getClanId());
if (clan == null)
{
return;
}
// Retrieve clans members, and store them under a String.
final StringBuffer membersList = new StringBuffer();
for (L2ClanMember player : clan.getMembers())
{
if (player != null)
{
if (membersList.length() > 0)
{
membersList.append(";");
}
membersList.append(player.getName());
}
}
MailBBSManager.getInstance().sendLetter(membersList.toString(), ar4, ar5, activeChar);
sendClanDetails(activeChar, activeChar.getClanId());
}
else
{
super.parseWrite(ar1, ar2, ar3, ar4, ar5, activeChar);
}
}
@Override
protected String getFolder()
{
return "clan/";
}
private static void sendClanMail(L2PcInstance activeChar, int clanId)
{
final L2Clan clan = ClanTable.getInstance().getClan(clanId);
if (clan == null)
{
return;
}
if ((activeChar.getClanId() != clanId) || !activeChar.isClanLeader())
{
activeChar.sendPacket(SystemMessageId.ONLY_THE_CLAN_LEADER_IS_ENABLED);
sendClanList(activeChar, 1);
return;
}
String content = HtmCache.getInstance().getHtm(CB_PATH + "clan/clanhome-mail.htm");
content = content.replaceAll("%clanid%", Integer.toString(clanId));
content = content.replaceAll("%clanName%", clan.getName());
separateAndSend(content, activeChar);
}
private static void sendClanManagement(L2PcInstance activeChar, int clanId)
{
final L2Clan clan = ClanTable.getInstance().getClan(clanId);
if (clan == null)
{
return;
}
if ((activeChar.getClanId() != clanId) || !activeChar.isClanLeader())
{
activeChar.sendPacket(SystemMessageId.ONLY_THE_CLAN_LEADER_IS_ENABLED);
sendClanList(activeChar, 1);
return;
}
String content = HtmCache.getInstance().getHtm(CB_PATH + "clan/clanhome-management.htm");
content = content.replaceAll("%clanid%", Integer.toString(clan.getClanId()));
send1001(content, activeChar);
send1002(activeChar, clan.getIntroduction(), "", "");
}
private static void sendClanNotice(L2PcInstance activeChar, int clanId)
{
final L2Clan clan = ClanTable.getInstance().getClan(clanId);
if ((clan == null) || (activeChar.getClanId() != clanId))
{
return;
}
if (clan.getLevel() < 2)
{
activeChar.sendPacket(SystemMessageId.NO_CB_IN_MY_CLAN);
sendClanList(activeChar, 1);
return;
}
String content = HtmCache.getInstance().getHtm(CB_PATH + "clan/clanhome-notice.htm");
content = content.replaceAll("%clanid%", Integer.toString(clan.getClanId()));
content = content.replace("%enabled%", "[" + String.valueOf(clan.isNoticeEnabled()) + "]");
content = content.replace("%flag%", String.valueOf(!clan.isNoticeEnabled()));
send1001(content, activeChar);
send1002(activeChar, clan.getNotice(), "", "");
}
private static void sendClanList(L2PcInstance activeChar, int index)
{
String content = HtmCache.getInstance().getHtm(CB_PATH + "clan/clanlist.htm");
// Player got a clan, show the associated header.
final StringBuilder sb = new StringBuilder();
final L2Clan playerClan = activeChar.getClan();
if (playerClan != null)
{
StringUtil.append(sb, "<table width=610 bgcolor=A7A19A><tr><td width=5></td><td width=605><a action=\"bypass _bbsclan;home;", playerClan.getClanId(), "\">[GO TO MY CLAN]</a></td></tr></table>");
}
content = content.replace("%homebar%", sb.toString());
if (index < 1)
{
index = 1;
}
// Cleanup sb.
sb.setLength(0);
// List of clans.
int i = 0;
for (L2Clan cl : ClanTable.getInstance().getClans())
{
if (i > ((index + 1) * 7))
{
break;
}
if (i++ >= ((index - 1) * 7))
{
StringUtil.append(sb, "<table width=610><tr><td width=5></td><td width=150 align=center><a action=\"bypass _bbsclan;home;", cl.getClanId(), "\">", cl.getName(), "</a></td><td width=150 align=center>", cl.getLeaderName(), "</td><td width=100 align=center>", cl.getLevel(), "</td><td width=200 align=center>", cl.getMembersCount(), "</td><td width=5></td></tr></table><br1><img src=\"L2UI.Squaregray\" width=605 height=1><br1>");
}
}
sb.append("<table><tr>");
if (index == 1)
{
sb.append("<td><button action=\"\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16></td>");
}
else
{
StringUtil.append(sb, "<td><button action=\"_bbsclan;clan;", index - 1, "\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16 ></td>");
}
i = 0;
int nbp = ClanTable.getInstance().getClans().length / 8;
if ((nbp * 8) != ClanTable.getInstance().getClans().length)
{
nbp++;
}
for (i = 1; i <= nbp; i++)
{
if (i == index)
{
StringUtil.append(sb, "<td> ", i, " </td>");
}
else
{
StringUtil.append(sb, "<td><a action=\"bypass _bbsclan;clan;", i, "\"> ", i, " </a></td>");
}
}
if (index == nbp)
{
sb.append("<td><button action=\"\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16></td>");
}
else
{
StringUtil.append(sb, "<td><button action=\"bypass _bbsclan;clan;", index + 1, "\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16 ></td>");
}
sb.append("</tr></table>");
content = content.replace("%clanlist%", sb.toString());
separateAndSend(content, activeChar);
}
private static void sendClanDetails(L2PcInstance activeChar, int clanId)
{
final L2Clan clan = ClanTable.getInstance().getClan(clanId);
if (clan == null)
{
return;
}
if (clan.getLevel() < 2)
{
activeChar.sendPacket(SystemMessageId.NO_CB_IN_MY_CLAN);
sendClanList(activeChar, 1);
return;
}
// Load different HTM following player case, 3 possibilites : randomer, member, clan leader.
String content;
if (activeChar.getClanId() != clanId)
{
content = HtmCache.getInstance().getHtm(CB_PATH + "clan/clanhome.htm");
}
else if (activeChar.isClanLeader())
{
content = HtmCache.getInstance().getHtm(CB_PATH + "clan/clanhome-leader.htm");
}
else
{
content = HtmCache.getInstance().getHtm(CB_PATH + "clan/clanhome-member.htm");
}
content = content.replaceAll("%clanid%", Integer.toString(clan.getClanId()));
content = content.replace("%clanIntro%", clan.getIntroduction());
content = content.replace("%clanName%", clan.getName());
content = content.replace("%clanLvL%", Integer.toString(clan.getLevel()));
content = content.replace("%clanMembers%", Integer.toString(clan.getMembersCount()));
content = content.replaceAll("%clanLeader%", clan.getLeaderName());
content = content.replace("%allyName%", (clan.getAllyId() > 0) ? clan.getAllyName() : "");
separateAndSend(content, activeChar);
}
private static class SingletonHolder
{
protected static final ClanBBSManager _instance = new ClanBBSManager();
}
}

View File

@ -0,0 +1,141 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.Manager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.text.SimpleDateFormat;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.cache.HtmCache;
import com.l2jmobius.gameserver.communitybbs.CommunityBoard;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.util.Util;
/**
* Favorite board.
* @author Zoey76
*/
public class FavoriteBBSManager extends BaseBBSManager
{
// SQL Queries
private static final String SELECT_FAVORITES = "SELECT * FROM `bbs_favorites` WHERE `playerId`=? ORDER BY `favAddDate` DESC";
private static final String DELETE_FAVORITE = "DELETE FROM `bbs_favorites` WHERE `playerId`=? AND `favId`=?";
private static final String ADD_FAVORITE = "REPLACE INTO `bbs_favorites`(`playerId`, `favTitle`, `favBypass`) VALUES(?, ?, ?)";
protected FavoriteBBSManager()
{
}
public static FavoriteBBSManager getInstance()
{
return SingletonHolder._instance;
}
@Override
public void parseCmd(String command, L2PcInstance activeChar)
{
// None of this commands can be added to favorites.
if (command.startsWith("_bbsgetfav"))
{
// Load Favorite links
final String list = HtmCache.getInstance().getHtm(CB_PATH + "favorite_list.html");
final StringBuilder sb = new StringBuilder();
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement(SELECT_FAVORITES))
{
ps.setInt(1, activeChar.getObjectId());
try (ResultSet rs = ps.executeQuery())
{
while (rs.next())
{
String link = list.replaceAll("%fav_bypass%", rs.getString("favBypass"));
link = link.replaceAll("%fav_title%", rs.getString("favTitle"));
final SimpleDateFormat date = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
link = link.replaceAll("%fav_add_date%", date.format(rs.getTimestamp("favAddDate")));
link = link.replaceAll("%fav_id%", String.valueOf(rs.getInt("favId")));
sb.append(link);
}
}
String html = HtmCache.getInstance().getHtm(CB_PATH + "favorite.html");
html = html.replaceAll("%fav_list%", sb.toString());
separateAndSend(html, activeChar);
}
catch (Exception e)
{
LOGGER.warning(FavoriteBBSManager.class.getSimpleName() + ": Couldn't load favorite links for player " + activeChar.getName());
}
}
else if (command.startsWith("bbs_add_fav"))
{
final String bypass = CommunityBoard.getInstance().removeBypass(activeChar);
if (bypass != null)
{
final String[] parts = bypass.split("&", 2);
if (parts.length != 2)
{
LOGGER.warning(FavoriteBBSManager.class.getSimpleName() + ": Couldn't add favorite link, " + bypass + " it's not a valid bypass!");
return;
}
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement(ADD_FAVORITE))
{
ps.setInt(1, activeChar.getObjectId());
ps.setString(2, parts[0].trim());
ps.setString(3, parts[1].trim());
ps.execute();
// Callback
parseCmd("_bbsgetfav", activeChar);
}
catch (Exception e)
{
LOGGER.warning(FavoriteBBSManager.class.getSimpleName() + ": Couldn't add favorite link " + command + " for player " + activeChar.getName());
}
}
}
else if (command.startsWith("_bbsdelfav_"))
{
final String favId = command.replaceAll("_bbsdelfav_", "");
if (!Util.isDigit(favId))
{
LOGGER.warning(FavoriteBBSManager.class.getSimpleName() + ": Couldn't delete favorite link, " + favId + " it's not a valid ID!");
return;
}
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement(DELETE_FAVORITE))
{
ps.setInt(1, activeChar.getObjectId());
ps.setInt(2, Integer.parseInt(favId));
ps.execute();
// Callback
parseCmd("_bbsgetfav", activeChar);
}
catch (Exception e)
{
LOGGER.warning(FavoriteBBSManager.class.getSimpleName() + ": Couldn't delete favorite link ID " + favId + " for player " + activeChar.getName());
}
}
}
private static class SingletonHolder
{
protected static final FavoriteBBSManager _instance = new FavoriteBBSManager();
}
}

View File

@ -0,0 +1,127 @@
/*
* 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.gameserver.communitybbs.Manager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.communitybbs.BB.Forum;
public class ForumsBBSManager extends BaseBBSManager
{
private final List<Forum> _table;
private int _lastid = 1;
public static ForumsBBSManager getInstance()
{
return SingletonHolder._instance;
}
protected ForumsBBSManager()
{
_table = new CopyOnWriteArrayList<>();
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT forum_id FROM forums WHERE forum_type=0");
ResultSet result = statement.executeQuery();
while (result.next())
{
addForum(new Forum(result.getInt("forum_id"), null));
}
result.close();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("Data error on Forum (root): " + e.getMessage());
}
}
public void initRoot()
{
for (Forum f : _table)
{
f.vload();
}
LOGGER.info("Loaded " + _table.size() + " forums. Last forum id used: " + _lastid);
}
public void addForum(Forum ff)
{
if (ff == null)
{
return;
}
_table.add(ff);
if (ff.getID() > _lastid)
{
_lastid = ff.getID();
}
}
public Forum getForumByName(String Name)
{
for (Forum f : _table)
{
if (f.getName().equals(Name))
{
return f;
}
}
return null;
}
public Forum createNewForum(String name, Forum parent, int type, int perm, int oid)
{
Forum forum = new Forum(name, parent, type, perm, oid);
forum.insertIntoDb();
return forum;
}
public int getANewID()
{
return ++_lastid;
}
public Forum getForumByID(int id)
{
for (Forum f : _table)
{
if (f.getID() == id)
{
return f;
}
}
return null;
}
private static class SingletonHolder
{
protected static final ForumsBBSManager _instance = new ForumsBBSManager();
}
}

View File

@ -0,0 +1,381 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.Manager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.commons.util.StringUtil;
import com.l2jmobius.gameserver.cache.HtmCache;
import com.l2jmobius.gameserver.communitybbs.CommunityBoard;
import com.l2jmobius.gameserver.datatables.sql.CharNameTable;
import com.l2jmobius.gameserver.model.BlockList;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.FriendList;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
public class FriendsBBSManager extends BaseBBSManager
{
private static final String FRIENDLIST_DELETE_BUTTON = "<br>\n<table><tr><td width=10></td><td>Are you sure you want to delete all friends from your Friends List?</td><td width=20></td><td><button value=\"OK\" action=\"bypass _friend;delall\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td></tr></table>";
private static final String BLOCKLIST_DELETE_BUTTON = "<br>\n<table><tr><td width=10></td><td>Are you sure you want to delete all players from your Block List?</td><td width=20></td><td><button value=\"OK\" action=\"bypass _block;delall\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td></tr></table>";
protected FriendsBBSManager()
{
}
public static FriendsBBSManager getInstance()
{
return SingletonHolder._instance;
}
@Override
public void parseCmd(String command, L2PcInstance activeChar)
{
if (command.startsWith("_friendlist"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Friends List", command);
showFriendsList(activeChar, false);
}
else if (command.startsWith("_blocklist"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Ignore List", command);
showBlockList(activeChar, false);
}
else if (command.startsWith("_friend"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
String action = st.nextToken();
if (action.equals("select"))
{
activeChar.selectFriend((st.hasMoreTokens()) ? Integer.valueOf(st.nextToken()) : 0);
showFriendsList(activeChar, false);
}
else if (action.equals("deselect"))
{
activeChar.deselectFriend((st.hasMoreTokens()) ? Integer.valueOf(st.nextToken()) : 0);
showFriendsList(activeChar, false);
}
else if (action.equals("delall"))
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("DELETE FROM character_friends WHERE char_id = ? OR friend_id = ?");
statement.setInt(1, activeChar.getObjectId());
statement.setInt(2, activeChar.getObjectId());
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("could not delete friends objectid: " + e);
}
for (int friendId : activeChar.getFriendList())
{
L2PcInstance player = L2World.getInstance().getPlayer(friendId);
if (player != null)
{
player.getFriendList().remove(Integer.valueOf(activeChar.getObjectId()));
player.getSelectedFriendList().remove(Integer.valueOf(activeChar.getObjectId()));
player.sendPacket(new FriendList(player)); // update friendList *heavy method*
}
}
activeChar.getFriendList().clear();
activeChar.getSelectedFriendList().clear();
showFriendsList(activeChar, false);
activeChar.sendMessage("You have cleared your friend list.");
activeChar.sendPacket(new FriendList(activeChar));
}
else if (action.equals("delconfirm"))
{
showFriendsList(activeChar, true);
}
else if (action.equals("del"))
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
for (int friendId : activeChar.getSelectedFriendList())
{
PreparedStatement statement = con.prepareStatement("DELETE FROM character_friends WHERE (char_id = ? AND friend_id = ?) OR (char_id = ? AND friend_id = ?)");
statement.setInt(1, activeChar.getObjectId());
statement.setInt(2, friendId);
statement.setInt(3, friendId);
statement.setInt(4, activeChar.getObjectId());
statement.execute();
statement.close();
String name = CharNameTable.getInstance().getPlayerName(friendId);
L2PcInstance player = L2World.getInstance().getPlayer(friendId);
if (player != null)
{
player.getFriendList().remove(Integer.valueOf(activeChar.getObjectId()));
player.sendPacket(new FriendList(player)); // update friendList *heavy method*
}
// Player deleted from your friendlist
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_DELETED_FROM_YOUR_FRIENDS_LIST).addString(name));
activeChar.getFriendList().remove(Integer.valueOf(friendId));
}
}
catch (Exception e)
{
LOGGER.warning("could not delete friend objectid: " + e);
}
activeChar.getSelectedFriendList().clear();
showFriendsList(activeChar, false);
activeChar.sendPacket(new FriendList(activeChar)); // update friendList *heavy method*
}
else if (action.equals("mail"))
{
if (!activeChar.getSelectedFriendList().isEmpty())
{
showMailWrite(activeChar);
}
}
}
else if (command.startsWith("_block"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
String action = st.nextToken();
if (action.equals("select"))
{
activeChar.selectBlock((st.hasMoreTokens()) ? Integer.valueOf(st.nextToken()) : 0);
showBlockList(activeChar, false);
}
else if (action.equals("deselect"))
{
activeChar.deselectBlock((st.hasMoreTokens()) ? Integer.valueOf(st.nextToken()) : 0);
showBlockList(activeChar, false);
}
else if (action.equals("delall"))
{
List<Integer> list = new ArrayList<>();
list.addAll(activeChar.getBlockList().getBlockList());
for (Integer blockId : list)
{
BlockList.removeFromBlockList(activeChar, blockId);
}
activeChar.getSelectedBlocksList().clear();
showBlockList(activeChar, false);
}
else if (action.equals("delconfirm"))
{
showBlockList(activeChar, true);
}
else if (action.equals("del"))
{
for (Integer blockId : activeChar.getSelectedBlocksList())
{
BlockList.removeFromBlockList(activeChar, blockId);
}
activeChar.getSelectedBlocksList().clear();
showBlockList(activeChar, false);
}
}
else
{
super.parseCmd(command, activeChar);
}
}
@Override
public void parseWrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
if (ar1.equalsIgnoreCase("mail"))
{
MailBBSManager.getInstance().sendLetter(ar2, ar4, ar5, activeChar);
showFriendsList(activeChar, false);
}
else
{
super.parseWrite(ar1, ar2, ar3, ar4, ar5, activeChar);
}
}
private static void showFriendsList(L2PcInstance activeChar, boolean delMsg)
{
String content = HtmCache.getInstance().getHtm(CB_PATH + "friend/friend-list.htm");
if (content == null)
{
return;
}
// Retrieve activeChar's friendlist and selected
final List<Integer> list = activeChar.getFriendList();
final List<Integer> slist = activeChar.getSelectedFriendList();
final StringBuilder sb = new StringBuilder();
// Friendlist
for (Integer id : list)
{
if (slist.contains(id))
{
continue;
}
final String friendName = CharNameTable.getInstance().getPlayerName(id);
if (friendName == null)
{
continue;
}
final L2PcInstance friend = L2World.getInstance().getPlayer(id);
StringUtil.append(sb, "<a action=\"bypass _friend;select;", id, "\">[Select]</a>&nbsp;", friendName, " ", (((friend != null) && (friend.isOnline() == 1)) ? "(on)" : "(off)"), "<br1>");
}
content = content.replaceAll("%friendslist%", sb.toString());
// Cleanup sb.
sb.setLength(0);
// Selected friendlist
for (Integer id : slist)
{
final String friendName = CharNameTable.getInstance().getPlayerName(id);
if (friendName == null)
{
continue;
}
final L2PcInstance friend = L2World.getInstance().getPlayer(id);
StringUtil.append(sb, "<a action=\"bypass _friend;deselect;", id, "\">[Deselect]</a>&nbsp;", friendName, " ", (((friend != null) && (friend.isOnline() == 1)) ? "(on)" : "(off)"), "<br1>");
}
content = content.replaceAll("%selectedFriendsList%", sb.toString());
// Delete button.
content = content.replaceAll("%deleteMSG%", (delMsg) ? FRIENDLIST_DELETE_BUTTON : "");
separateAndSend(content, activeChar);
}
private static void showBlockList(L2PcInstance activeChar, boolean delMsg)
{
String content = HtmCache.getInstance().getHtm(CB_PATH + "friend/friend-blocklist.htm");
if (content == null)
{
return;
}
// Retrieve activeChar's blocklist and selected
final List<Integer> list = activeChar.getBlockList().getBlockList();
final List<Integer> slist = activeChar.getSelectedBlocksList();
final StringBuilder sb = new StringBuilder();
// Blocklist
for (Integer id : list)
{
if (slist.contains(id))
{
continue;
}
final String blockName = CharNameTable.getInstance().getPlayerName(id);
if (blockName == null)
{
continue;
}
final L2PcInstance block = L2World.getInstance().getPlayer(id);
StringUtil.append(sb, "<a action=\"bypass _block;select;", id, "\">[Select]</a>&nbsp;", blockName, " ", (((block != null) && (block.isOnline() == 1)) ? "(on)" : "(off)"), "<br1>");
}
content = content.replaceAll("%blocklist%", sb.toString());
// Cleanup sb.
sb.setLength(0);
// Selected Blocklist
for (Integer id : slist)
{
final String blockName = CharNameTable.getInstance().getPlayerName(id);
if (blockName == null)
{
continue;
}
final L2PcInstance block = L2World.getInstance().getPlayer(id);
StringUtil.append(sb, "<a action=\"bypass _block;deselect;", id, "\">[Deselect]</a>&nbsp;", blockName, " ", (((block != null) && (block.isOnline() == 1)) ? "(on)" : "(off)"), "<br1>");
}
content = content.replaceAll("%selectedBlocksList%", sb.toString());
// Delete button.
content = content.replaceAll("%deleteMSG%", (delMsg) ? BLOCKLIST_DELETE_BUTTON : "");
separateAndSend(content, activeChar);
}
public static final void showMailWrite(L2PcInstance activeChar)
{
String content = HtmCache.getInstance().getHtm(CB_PATH + "friend/friend-mail.htm");
if (content == null)
{
return;
}
final StringBuilder sb = new StringBuilder();
for (int id : activeChar.getSelectedFriendList())
{
String friendName = CharNameTable.getInstance().getPlayerName(id);
if (friendName == null)
{
continue;
}
if (sb.length() > 0)
{
sb.append(";");
}
sb.append(friendName);
}
content = content.replaceAll("%list%", sb.toString());
separateAndSend(content, activeChar);
}
@Override
protected String getFolder()
{
return "friend/";
}
private static class SingletonHolder
{
protected static final FriendsBBSManager _instance = new FriendsBBSManager();
}
}

View File

@ -0,0 +1,868 @@
/*
* 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.gameserver.communitybbs.Manager;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.commons.util.StringUtil;
import com.l2jmobius.gameserver.cache.HtmCache;
import com.l2jmobius.gameserver.communitybbs.CommunityBoard;
import com.l2jmobius.gameserver.datatables.sql.CharNameTable;
import com.l2jmobius.gameserver.model.BlockList;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.ExMailArrived;
import com.l2jmobius.gameserver.network.serverpackets.PlaySound;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* @author JIV, Johan, Vital
*/
public class MailBBSManager extends BaseBBSManager
{
private enum MailType
{
INBOX("Inbox", "<a action=\"bypass _bbsmail\">Inbox</a>"),
SENTBOX("Sent Box", "<a action=\"bypass _bbsmail;sentbox\">Sent Box</a>"),
ARCHIVE("Mail Archive", "<a action=\"bypass _bbsmail;archive\">Mail Archive</a>"),
TEMPARCHIVE("Temporary Mail Archive", "<a action=\"bypass _bbsmail;temp_archive\">Temporary Mail Archive</a>");
private final String _description;
private final String _bypass;
private MailType(String description, String bypass)
{
_description = description;
_bypass = bypass;
}
public String getDescription()
{
return _description;
}
public String getBypass()
{
return _bypass;
}
public static final MailType[] VALUES = values();
}
private final Map<Integer, List<Mail>> _mails = new HashMap<>();
private int _lastid = 0;
private static final String SELECT_CHAR_MAILS = "SELECT * FROM character_mail WHERE charId = ? ORDER BY letterId ASC";
private static final String INSERT_NEW_MAIL = "INSERT INTO character_mail (charId, letterId, senderId, location, recipientNames, subject, message, sentDate, unread) VALUES (?,?,?,?,?,?,?,?,?)";
private static final String DELETE_MAIL = "DELETE FROM character_mail WHERE letterId = ?";
private static final String MARK_MAIL_READ = "UPDATE character_mail SET unread = ? WHERE letterId = ?";
private static final String SET_LETTER_LOC = "UPDATE character_mail SET location = ? WHERE letterId = ?";
private static final String SELECT_LAST_ID = "SELECT letterId FROM character_mail ORDER BY letterId DESC LIMIT 1";
public class Mail
{
int charId;
int letterId;
int senderId;
MailType location;
String recipientNames;
String subject;
String message;
Timestamp sentDate;
String sentDateString;
boolean unread;
}
public static MailBBSManager getInstance()
{
return SingletonHolder._instance;
}
protected MailBBSManager()
{
initId();
}
@Override
public void parseCmd(String command, L2PcInstance activeChar)
{
CommunityBoard.getInstance().addBypass(activeChar, "Mail Command", command);
if (command.equals("_bbsmail") || command.equals("_maillist_0_1_0_"))
{
showMailList(activeChar, 1, MailType.INBOX);
}
else if (command.startsWith("_bbsmail"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
String action = st.nextToken();
if (action.equals("inbox") || action.equals("sentbox") || action.equals("archive") || action.equals("temparchive"))
{
final int page = (st.hasMoreTokens()) ? Integer.parseInt(st.nextToken()) : 1;
final String sType = (st.hasMoreTokens()) ? st.nextToken() : "";
final String search = (st.hasMoreTokens()) ? st.nextToken() : "";
showMailList(activeChar, page, Enum.valueOf(MailType.class, action.toUpperCase()), sType, search);
}
else if (action.equals("crea"))
{
showWriteView(activeChar);
}
else if (action.equals("view"))
{
final int letterId = (st.hasMoreTokens()) ? Integer.parseInt(st.nextToken()) : -1;
Mail letter = getLetter(activeChar, letterId);
if (letter == null)
{
showLastForum(activeChar);
}
else
{
showLetterView(activeChar, letter);
if (letter.unread)
{
setLetterToRead(activeChar, letter.letterId);
}
}
}
else if (action.equals("reply"))
{
final int letterId = (st.hasMoreTokens()) ? Integer.parseInt(st.nextToken()) : -1;
Mail letter = getLetter(activeChar, letterId);
if (letter == null)
{
showLastForum(activeChar);
}
else
{
showWriteView(activeChar, getCharName(letter.senderId), letter);
}
}
else if (action.equals("del"))
{
final int letterId = (st.hasMoreTokens()) ? Integer.parseInt(st.nextToken()) : -1;
Mail letter = getLetter(activeChar, letterId);
if (letter != null)
{
deleteLetter(activeChar, letter.letterId);
}
showLastForum(activeChar);
}
else if (action.equals("store"))
{
final int letterId = (st.hasMoreTokens()) ? Integer.parseInt(st.nextToken()) : -1;
Mail letter = getLetter(activeChar, letterId);
if (letter != null)
{
setLetterLocation(activeChar, letter.letterId, MailType.ARCHIVE);
}
showMailList(activeChar, 1, MailType.ARCHIVE);
}
}
else
{
super.parseCmd(command, activeChar);
}
}
@Override
public void parseWrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
if (ar1.equals("Send"))
{
sendLetter(ar3, ar4, ar5, activeChar);
showMailList(activeChar, 1, MailType.SENTBOX);
}
else if (ar1.startsWith("Search"))
{
StringTokenizer st = new StringTokenizer(ar1, ";");
st.nextToken();
showMailList(activeChar, 1, Enum.valueOf(MailType.class, st.nextToken().toUpperCase()), ar4, ar5);
}
else
{
super.parseWrite(ar1, ar2, ar3, ar4, ar5, activeChar);
}
}
private void initId()
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement(SELECT_LAST_ID);
ResultSet result = statement.executeQuery();
while (result.next())
{
if (result.getInt(1) > _lastid)
{
_lastid = result.getInt(1);
}
}
result.close();
statement.close();
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": data error on MailBBS (initId): " + e);
e.printStackTrace();
}
}
private synchronized int getNewMailId()
{
return ++_lastid;
}
private List<Mail> getPlayerMails(int objId)
{
List<Mail> _letters = _mails.get(objId);
if (_letters == null)
{
_letters = new ArrayList<>();
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement(SELECT_CHAR_MAILS);
statement.setInt(1, objId);
ResultSet result = statement.executeQuery();
while (result.next())
{
Mail letter = new Mail();
letter.charId = result.getInt("charId");
letter.letterId = result.getInt("letterId");
letter.senderId = result.getInt("senderId");
letter.location = Enum.valueOf(MailType.class, result.getString("location").toUpperCase());
letter.recipientNames = result.getString("recipientNames");
letter.subject = result.getString("subject");
letter.message = result.getString("message");
letter.sentDate = result.getTimestamp("sentDate");
letter.sentDateString = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(letter.sentDate);
letter.unread = result.getInt("unread") != 0;
_letters.add(0, letter);
}
result.close();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("couldnt load mail for ID:" + objId + " " + e.getMessage());
}
_mails.put(objId, _letters);
}
return _letters;
}
private Mail getLetter(L2PcInstance activeChar, int letterId)
{
for (Mail letter : getPlayerMails(activeChar.getObjectId()))
{
if (letter.letterId == letterId)
{
return letter;
}
}
return null;
}
private static String abbreviate(String s, int maxWidth)
{
return s.length() > maxWidth ? s.substring(0, maxWidth) : s;
}
public int checkUnreadMail(L2PcInstance activeChar)
{
int count = 0;
for (Mail letter : getPlayerMails(activeChar.getObjectId()))
{
if (letter.unread)
{
count++;
}
}
return count;
}
private void showMailList(L2PcInstance activeChar, int page, MailType type)
{
showMailList(activeChar, page, type, "", "");
}
private void showMailList(L2PcInstance activeChar, int page, MailType type, String sType, String search)
{
List<Mail> letters;
if (!sType.equals("") && !search.equals(""))
{
letters = new ArrayList<>();
boolean byTitle = sType.equalsIgnoreCase("title");
for (Mail letter : getPlayerMails(activeChar.getObjectId()))
{
if (byTitle && letter.subject.toLowerCase().contains(search.toLowerCase()))
{
letters.add(letter);
}
else if (!byTitle)
{
String writer = getCharName(letter.senderId);
if (writer.toLowerCase().contains(search.toLowerCase()))
{
letters.add(letter);
}
}
}
}
else
{
letters = getPlayerMails(activeChar.getObjectId());
}
final int countMails = getCountLetters(activeChar.getObjectId(), type, sType, search);
final int maxpage = getMaxPageId(countMails);
if (page > maxpage)
{
page = maxpage;
}
if (page < 1)
{
page = 1;
}
activeChar.setMailPosition(page);
int index = 0, minIndex = 0, maxIndex = 0;
maxIndex = (page == 1 ? page * 9 : (page * 10) - 1);
minIndex = maxIndex - 9;
String content = HtmCache.getInstance().getHtm(CB_PATH + "mail/mail.htm");
content = content.replace("%inbox%", Integer.toString(getCountLetters(activeChar.getObjectId(), MailType.INBOX, "", "")));
content = content.replace("%sentbox%", Integer.toString(getCountLetters(activeChar.getObjectId(), MailType.SENTBOX, "", "")));
content = content.replace("%archive%", Integer.toString(getCountLetters(activeChar.getObjectId(), MailType.ARCHIVE, "", "")));
content = content.replace("%temparchive%", Integer.toString(getCountLetters(activeChar.getObjectId(), MailType.TEMPARCHIVE, "", "")));
content = content.replace("%type%", type.getDescription());
content = content.replace("%htype%", type.toString().toLowerCase());
final StringBuilder sb = new StringBuilder();
for (Mail letter : letters)
{
if (letter.location.equals(type))
{
if (index < minIndex)
{
index++;
continue;
}
if (index > maxIndex)
{
break;
}
StringUtil.append(sb, "<table width=610><tr><td width=5></td><td width=150>", getCharName(letter.senderId), "</td><td width=300><a action=\"bypass _bbsmail;view;", letter.letterId, "\">");
if (letter.unread)
{
sb.append("<font color=\"LEVEL\">");
}
sb.append(abbreviate(letter.subject, 51));
if (letter.unread)
{
sb.append("</font>");
}
StringUtil.append(sb, "</a></td><td width=150>", letter.sentDateString, "</td><td width=5></td></tr></table><img src=\"L2UI.Squaregray\" width=610 height=1>");
index++;
}
}
content = content.replace("%maillist%", sb.toString());
// CLeanup sb.
sb.setLength(0);
final String fullSearch = (!sType.equals("") && !search.equals("")) ? ";" + sType + ";" + search : "";
StringUtil.append(sb, "<td><table><tr><td></td></tr><tr><td><button action=\"bypass _bbsmail;", type, ";", (page == 1 ? page : page - 1), fullSearch, "\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16></td></tr></table></td>");
int i = 0;
if (maxpage > 21)
{
if (page <= 11)
{
for (i = 1; i <= (10 + page); i++)
{
if (i == page)
{
StringUtil.append(sb, "<td> ", i, " </td>");
}
else
{
StringUtil.append(sb, "<td><a action=\"bypass _bbsmail;", type, ";", i, fullSearch, "\"> ", i, " </a></td>");
}
}
}
else if ((page > 11) && ((maxpage - page) > 10))
{
for (i = (page - 10); i <= (page - 1); i++)
{
if (i == page)
{
continue;
}
StringUtil.append(sb, "<td><a action=\"bypass _bbsmail;", type, ";", i, fullSearch, "\"> ", i, " </a></td>");
}
for (i = page; i <= (page + 10); i++)
{
if (i == page)
{
StringUtil.append(sb, "<td> ", i, " </td>");
}
else
{
StringUtil.append(sb, "<td><a action=\"bypass _bbsmail;", type, ";", i, fullSearch, "\"> ", i, " </a></td>");
}
}
}
else if ((maxpage - page) <= 10)
{
for (i = (page - 10); i <= maxpage; i++)
{
if (i == page)
{
StringUtil.append(sb, "<td> ", i, " </td>");
}
else
{
StringUtil.append(sb, "<td><a action=\"bypass _bbsmail;", type, ";", i, fullSearch, "\"> ", i, " </a></td>");
}
}
}
}
else
{
for (i = 1; i <= maxpage; i++)
{
if (i == page)
{
StringUtil.append(sb, "<td> ", i, " </td>");
}
else
{
StringUtil.append(sb, "<td><a action=\"bypass _bbsmail;", type, ";", i, fullSearch, "\"> ", i, " </a></td>");
}
}
}
StringUtil.append(sb, "<td><table><tr><td></td></tr><tr><td><button action=\"bypass _bbsmail;", type, ";", (page == maxpage ? page : page + 1), fullSearch, "\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16 ></td></tr></table></td>");
content = content.replace("%maillistlength%", sb.toString());
separateAndSend(content, activeChar);
}
private void showLetterView(L2PcInstance activeChar, Mail letter)
{
if (letter == null)
{
showMailList(activeChar, 1, MailType.INBOX);
return;
}
String content = HtmCache.getInstance().getHtm(CB_PATH + "mail/mail-show.htm");
String link = letter.location.getBypass() + "&nbsp;&gt;&nbsp;" + letter.subject;
content = content.replace("%maillink%", link);
content = content.replace("%writer%", getCharName(letter.senderId));
content = content.replace("%sentDate%", letter.sentDateString);
content = content.replace("%receiver%", letter.recipientNames);
content = content.replace("%delDate%", "Unknown");
content = content.replace("%title%", letter.subject.replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;"));
content = content.replace("%mes%", letter.message.replaceAll("\r\n", "<br>").replaceAll("<", "&lt;").replaceAll(">", "&gt;").replaceAll("\"", "&quot;"));
content = content.replace("%letterId%", letter.letterId + "");
separateAndSend(content, activeChar);
}
private static void showWriteView(L2PcInstance activeChar)
{
String content = HtmCache.getInstance().getHtm(CB_PATH + "mail/mail-write.htm");
separateAndSend(content, activeChar);
}
private static void showWriteView(L2PcInstance activeChar, String parcipientName, Mail letter)
{
String content = HtmCache.getInstance().getHtm(CB_PATH + "mail/mail-reply.htm");
String link = letter.location.getBypass() + "&nbsp;&gt;&nbsp;<a action=\"bypass _bbsmail;view;" + letter.letterId + "\">" + letter.subject + "</a>&nbsp;&gt;&nbsp;";
content = content.replace("%maillink%", link);
content = content.replace("%recipients%", letter.senderId == activeChar.getObjectId() ? letter.recipientNames : getCharName(letter.senderId));
content = content.replace("%letterId%", letter.letterId + "");
send1001(content, activeChar);
send1002(activeChar, " ", "Re: " + letter.subject, "0");
}
public void sendLetter(String recipients, String subject, String message, L2PcInstance activeChar)
{
// Current time.
final long currentDate = Calendar.getInstance().getTimeInMillis();
// Get the current time - 1 day under timestamp format.
final Timestamp ts = new Timestamp(currentDate - 86400000L);
// Check sender mails based on previous timestamp. If more than 10 mails have been found for today, then cancel the use.
if (getPlayerMails(activeChar.getObjectId()).stream().filter(l -> l.sentDate.after(ts) && (l.location == MailType.SENTBOX)).count() >= 10)
{
activeChar.sendPacket(SystemMessageId.NO_MORE_MESSAGES_TODAY);
return;
}
// Format recipient names. If more than 5 are found, cancel the mail.
final String[] recipientNames = recipients.trim().split(";");
if ((recipientNames.length > 5) && !activeChar.isGM())
{
activeChar.sendPacket(SystemMessageId.ONLY_FIVE_RECIPIENTS);
return;
}
// Edit subject, if none.
if ((subject == null) || subject.isEmpty())
{
subject = "(no subject)";
}
// Edit message.
message = message.replaceAll("\n", "<br1>");
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
// Get the current time under timestamp format.
final Timestamp time = new Timestamp(currentDate);
PreparedStatement statement = null;
for (String recipientName : recipientNames)
{
// Recipient is an invalid player, or is the sender.
final int recipientId = CharNameTable.getInstance().getPlayerObjectId(recipientName);
if ((recipientId <= 0) || (recipientId == activeChar.getObjectId()))
{
activeChar.sendPacket(SystemMessageId.INCORRECT_TARGET);
continue;
}
final L2PcInstance recipientPlayer = L2World.getInstance().getPlayer(recipientId);
if (!activeChar.isGM())
{
// Sender is a regular player, while recipient is a GM.
if (isGM(recipientId))
{
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_MAIL_GM_S1).addString(recipientName));
continue;
}
// The recipient is on block mode.
if (isBlocked(activeChar, recipientId))
{
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_BLOCKED_YOU_CANNOT_MAIL).addString(recipientName));
continue;
}
// The recipient box is already full.
if (isRecipInboxFull(recipientId))
{
activeChar.sendPacket(SystemMessageId.MESSAGE_NOT_SENT);
if (recipientPlayer != null)
{
recipientPlayer.sendPacket(SystemMessageId.MAILBOX_FULL);
}
continue;
}
}
final int id = getNewMailId();
if (statement == null)
{
statement = con.prepareStatement(INSERT_NEW_MAIL);
statement.setInt(3, activeChar.getObjectId());
statement.setString(4, "inbox");
statement.setString(5, recipients);
statement.setString(6, abbreviate(subject, 128));
statement.setString(7, message);
statement.setTimestamp(8, time);
statement.setInt(9, 1);
}
statement.setInt(1, recipientId);
statement.setInt(2, id);
statement.execute();
final Mail letter = new Mail();
letter.charId = recipientId;
letter.letterId = id;
letter.senderId = activeChar.getObjectId();
letter.location = MailType.INBOX;
letter.recipientNames = recipients;
letter.subject = abbreviate(subject, 128);
letter.message = message;
letter.sentDate = time;
letter.sentDateString = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(letter.sentDate);
letter.unread = true;
getPlayerMails(recipientId).add(0, letter);
if (recipientPlayer != null)
{
recipientPlayer.sendPacket(SystemMessageId.NEW_MAIL);
recipientPlayer.sendPacket(new PlaySound("systemmsg_e.1233"));
recipientPlayer.sendPacket(ExMailArrived.STATIC_PACKET);
}
}
// Create a copy into activeChar's sent box, if at least one recipient has been reached.
if (statement != null)
{
final int id = getNewMailId();
statement.setInt(1, activeChar.getObjectId());
statement.setInt(2, id);
statement.setString(4, "sentbox");
statement.setInt(9, 0);
statement.execute();
statement.close();
final Mail letter = new Mail();
letter.charId = activeChar.getObjectId();
letter.letterId = id;
letter.senderId = activeChar.getObjectId();
letter.location = MailType.SENTBOX;
letter.recipientNames = recipients;
letter.subject = abbreviate(subject, 128);
letter.message = message;
letter.sentDate = time;
letter.sentDateString = new SimpleDateFormat("yyyy-MM-dd HH:mm").format(letter.sentDate);
letter.unread = false;
getPlayerMails(activeChar.getObjectId()).add(0, letter);
activeChar.sendPacket(SystemMessageId.SENT_MAIL);
}
}
catch (Exception e)
{
LOGGER.warning("couldnt send letter for " + activeChar.getName() + " " + e.getMessage());
}
}
private int getCountLetters(int objId, MailType location, String sType, String search)
{
int count = 0;
if (!sType.equals("") && !search.equals(""))
{
boolean byTitle = sType.equalsIgnoreCase("title");
for (Mail letter : getPlayerMails(objId))
{
if (!letter.location.equals(location))
{
continue;
}
if (byTitle && letter.subject.toLowerCase().contains(search.toLowerCase()))
{
count++;
}
else if (!byTitle)
{
String writer = getCharName(letter.senderId);
if (writer.toLowerCase().contains(search.toLowerCase()))
{
count++;
}
}
}
}
else
{
for (Mail letter : getPlayerMails(objId))
{
if (letter.location.equals(location))
{
count++;
}
}
}
return count;
}
private static boolean isBlocked(L2PcInstance activeChar, int recipId)
{
for (L2PcInstance player : L2World.getInstance().getAllPlayers())
{
if (player.getObjectId() == recipId)
{
if (BlockList.isInBlockList(player, activeChar))
{
return true;
}
return false;
}
}
return false;
}
private void deleteLetter(L2PcInstance activeChar, int letterId)
{
for (Mail letter : getPlayerMails(activeChar.getObjectId()))
{
if (letter.letterId == letterId)
{
getPlayerMails(activeChar.getObjectId()).remove(letter);
break;
}
}
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement(DELETE_MAIL);
statement.setInt(1, letterId);
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("couldnt delete letter " + letterId + " " + e);
}
}
private void setLetterToRead(L2PcInstance activeChar, int letterId)
{
getLetter(activeChar, letterId).unread = false;
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement(MARK_MAIL_READ);
statement.setInt(1, 0);
statement.setInt(2, letterId);
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("couldnt set unread to false for " + letterId + " " + e);
}
}
private void setLetterLocation(L2PcInstance activeChar, int letterId, MailType location)
{
getLetter(activeChar, letterId).location = location;
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement(SET_LETTER_LOC);
statement.setString(1, location.toString().toLowerCase());
statement.setInt(2, letterId);
statement.execute();
statement.close();
}
catch (Exception e)
{
LOGGER.warning("couldnt set location to false for " + letterId + " " + e);
}
}
private static String getCharName(int charId)
{
String name = CharNameTable.getInstance().getPlayerName(charId);
return name == null ? "Unknown" : name;
}
private static boolean isGM(int charId)
{
boolean isGM = false;
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT accesslevel FROM characters WHERE obj_Id = ?");
statement.setInt(1, charId);
ResultSet result = statement.executeQuery();
result.next();
isGM = result.getInt(1) > 0;
result.close();
statement.close();
}
catch (Exception e)
{
LOGGER.warning(e.getMessage());
}
return isGM;
}
private boolean isRecipInboxFull(int charId)
{
return getCountLetters(charId, MailType.INBOX, "", "") >= 100;
}
private void showLastForum(L2PcInstance activeChar)
{
final int page = activeChar.getMailPosition() % 1000;
final int type = activeChar.getMailPosition() / 1000;
showMailList(activeChar, page, MailType.VALUES[type]);
}
private static int getMaxPageId(int letterCount)
{
if (letterCount < 1)
{
return 1;
}
if ((letterCount % 10) == 0)
{
return letterCount / 10;
}
return (letterCount / 10) + 1;
}
private static class SingletonHolder
{
protected static final MailBBSManager _instance = new MailBBSManager();
}
}

View File

@ -0,0 +1,218 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.communitybbs.Manager;
import java.text.DateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.StringTokenizer;
import com.l2jmobius.gameserver.communitybbs.CommunityBoard;
import com.l2jmobius.gameserver.communitybbs.BB.Forum;
import com.l2jmobius.gameserver.communitybbs.BB.Post;
import com.l2jmobius.gameserver.communitybbs.BB.Topic;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
public class PostBBSManager extends BaseBBSManager
{
private final Map<Topic, Post> _postByTopic;
public static PostBBSManager getInstance()
{
return SingletonHolder._instance;
}
protected PostBBSManager()
{
_postByTopic = new HashMap<>();
}
@Override
public void parseCmd(String command, L2PcInstance activeChar)
{
if (command.startsWith("_bbsposts;read;"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Posts Command", command);
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
int idp = Integer.parseInt(st.nextToken());
String index = null;
if (st.hasMoreTokens())
{
index = st.nextToken();
}
int ind = 0;
if (index == null)
{
ind = 1;
}
else
{
ind = Integer.parseInt(index);
}
showPost((TopicBBSManager.getInstance().getTopicByID(idp)), ForumsBBSManager.getInstance().getForumByID(idf), activeChar, ind);
}
else if (command.startsWith("_bbsposts;edit;"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
int idt = Integer.parseInt(st.nextToken());
int idp = Integer.parseInt(st.nextToken());
showEditPost((TopicBBSManager.getInstance().getTopicByID(idt)), ForumsBBSManager.getInstance().getForumByID(idf), activeChar, idp);
}
else
{
super.parseCmd(command, activeChar);
}
}
@Override
public void parseWrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
StringTokenizer st = new StringTokenizer(ar1, ";");
int idf = Integer.parseInt(st.nextToken());
int idt = Integer.parseInt(st.nextToken());
int idp = Integer.parseInt(st.nextToken());
final Forum forum = ForumsBBSManager.getInstance().getForumByID(idf);
if (forum == null)
{
separateAndSend("<html><body><br><br><center>The forum named '" + idf + "' doesn't exist.</center></body></html>", activeChar);
return;
}
final Topic topic = forum.getTopic(idt);
if (topic == null)
{
separateAndSend("<html><body><br><br><center>The topic named '" + idt + "' doesn't exist.</center></body></html>", activeChar);
return;
}
final Post post = getPostByTopic(topic);
if (post.getCPost(idp) == null)
{
separateAndSend("<html><body><br><br><center>The post named '" + idp + "' doesn't exist.</center></body></html>", activeChar);
return;
}
post.getCPost(idp).postTxt = ar4;
post.updateText(idp);
parseCmd("_bbsposts;read;" + forum.getID() + ";" + topic.getID(), activeChar);
}
public Post getPostByTopic(Topic t)
{
Post post = _postByTopic.get(t);
if (post == null)
{
post = load(t);
_postByTopic.put(t, post);
}
return post;
}
public void delPostByTopic(Topic t)
{
_postByTopic.remove(t);
}
public void addPostByTopic(Post p, Topic t)
{
if (_postByTopic.get(t) == null)
{
_postByTopic.put(t, p);
}
}
private static Post load(Topic t)
{
return new Post(t);
}
private void showEditPost(Topic topic, Forum forum, L2PcInstance activeChar, int idp)
{
if ((forum == null) || (topic == null))
{
separateAndSend("<html><body><br><br><center>This forum and/or topic don't exit.</center></body></html>", activeChar);
return;
}
Post p = getPostByTopic(topic);
if (p == null)
{
separateAndSend("<html><body><br><br><center>This post doesn't exist.</center></body></html>", activeChar);
return;
}
showHtmlEditPost(topic, activeChar, forum, p);
}
private void showPost(Topic topic, Forum forum, L2PcInstance activeChar, int ind)
{
if ((forum == null) || (topic == null))
{
separateAndSend("<html><body><br><br><center>This forum and/or topic don't exist.</center></body></html>", activeChar);
}
else if (forum.getType() == Forum.MEMO)
{
showMemoPost(topic, activeChar, forum);
}
else
{
separateAndSend("<html><body><br><br><center>The forum named '" + forum.getName() + "' isn't implemented.</center></body></html>", activeChar);
}
}
private static void showHtmlEditPost(Topic topic, L2PcInstance activeChar, Forum forum, Post p)
{
final String html = "<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">" + forum.getName() + " Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0><tr><td width=610><img src=\"sek.cbui355\" width=\"610\" height=\"1\"><br1><img src=\"sek.cbui355\" width=\"610\" height=\"1\"></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=20></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&$413;</td><td FIXWIDTH=540>" + topic.getName() + "</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29 valign=top>&$427;</td><td align=center FIXWIDTH=540><MultiEdit var =\"Content\" width=535 height=313></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&nbsp;</td><td align=center FIXWIDTH=70><button value=\"&$140;\" action=\"Write Post " + forum.getID() + ";" + topic.getID() + ";0 _ Content Content Content\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td><td align=center FIXWIDTH=70><button value = \"&$141;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"> </td><td align=center FIXWIDTH=400>&nbsp;</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table></center></body></html>";
send1001(html, activeChar);
send1002(activeChar, p.getCPost(0).postTxt, topic.getName(), DateFormat.getInstance().format(new Date(topic.getDate())));
}
private void showMemoPost(Topic topic, L2PcInstance activeChar, Forum forum)
{
Post p = getPostByTopic(topic);
Locale locale = Locale.getDefault();
DateFormat dateFormat = DateFormat.getDateInstance(DateFormat.FULL, locale);
String mes = p.getCPost(0).postTxt.replace(">", "&gt;");
mes = mes.replace("<", "&lt;");
mes = mes.replace("\n", "<br1>");
final String html = "<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0 bgcolor=333333><tr><td height=10></td></tr><tr><td fixWIDTH=55 align=right valign=top>&$413; : &nbsp;</td><td fixWIDTH=380 valign=top>" + topic.getName() + "</td><td fixwidth=5></td><td fixwidth=50></td><td fixWIDTH=120></td></tr><tr><td height=10></td></tr><tr><td align=right><font color=\"AAAAAA\" >&$417; : &nbsp;</font></td><td><font color=\"AAAAAA\">" + topic.getOwnerName() + "</font></td><td></td><td><font color=\"AAAAAA\">&$418; :</font></td><td><font color=\"AAAAAA\">" + dateFormat.format(p.getCPost(0).postDate) + "</font></td></tr><tr><td height=10></td></tr></table><br><table border=0 cellspacing=0 cellpadding=0><tr><td fixwidth=5></td><td FIXWIDTH=600 align=left>" + mes + "</td><td fixqqwidth=5></td></tr></table><br><img src=\"L2UI.squareblank\" width=\"1\" height=\"5\"><img src=\"L2UI.squaregray\" width=\"610\" height=\"1\"><img src=\"L2UI.squareblank\" width=\"1\" height=\"5\"><table border=0 cellspacing=0 cellpadding=0 FIXWIDTH=610><tr><td width=50><button value=\"&$422;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td><td width=560 align=right><table border=0 cellspacing=0><tr><td FIXWIDTH=300></td><td><button value = \"&$424;\" action=\"bypass _bbsposts;edit;" + forum.getID() + ";" + topic.getID() + ";0\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;<td><button value = \"&$425;\" action=\"bypass _bbstopics;del;" + forum.getID() + ";" + topic.getID() + "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;<td><button value = \"&$421;\" action=\"bypass _bbstopics;crea;" + forum.getID() + "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td>&nbsp;</tr></table></td></tr></table><br><br><br></center></body></html>";
separateAndSend(html, activeChar);
}
private static class SingletonHolder
{
protected static final PostBBSManager _instance = new PostBBSManager();
}
}

View File

@ -0,0 +1,123 @@
/*
* 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.gameserver.communitybbs.Manager;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.StringTokenizer;
import com.l2jmobius.commons.util.StringUtil;
import com.l2jmobius.gameserver.cache.HtmCache;
import com.l2jmobius.gameserver.communitybbs.CommunityBoard;
import com.l2jmobius.gameserver.datatables.sql.ClanTable;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.ClanHallManager;
import com.l2jmobius.gameserver.model.L2Clan;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.entity.ClanHall;
import com.l2jmobius.gameserver.model.entity.siege.Castle;
public class RegionBBSManager extends BaseBBSManager
{
protected RegionBBSManager()
{
}
public static RegionBBSManager getInstance()
{
return SingletonHolder._instance;
}
@Override
public void parseCmd(String command, L2PcInstance activeChar)
{
if (command.equals("_bbsloc"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Region>", command);
showRegionsList(activeChar);
}
else if (command.startsWith("_bbsloc"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Region>", command);
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
showRegion(activeChar, Integer.parseInt(st.nextToken()));
}
else
{
super.parseCmd(command, activeChar);
}
}
@Override
protected String getFolder()
{
return "region/";
}
private static void showRegionsList(L2PcInstance activeChar)
{
final String content = HtmCache.getInstance().getHtm(CB_PATH + "region/castlelist.htm");
final StringBuilder sb = new StringBuilder(500);
for (Castle castle : CastleManager.getInstance().getCastles())
{
final L2Clan owner = ClanTable.getInstance().getClan(castle.getOwnerId());
StringUtil.append(sb, "<table><tr><td width=5></td><td width=160><a action=\"bypass _bbsloc;", castle.getCastleId(), "\">", castle.getName(), "</a></td><td width=160>", ((owner != null) ? "<a action=\"bypass _bbsclan;home;" + owner.getClanId() + "\">" + owner.getName() + "</a>" : "None"), "</td><td width=160>", (((owner != null) && (owner.getAllyId() > 0)) ? owner.getAllyName() : "None"), "</td><td width=120>", ((owner != null) ? castle.getTaxPercent() : "0"), "</td><td width=5></td></tr></table><br1><img src=\"L2UI.Squaregray\" width=605 height=1><br1>");
}
separateAndSend(content.replace("%castleList%", sb.toString()), activeChar);
}
private static void showRegion(L2PcInstance activeChar, int castleId)
{
final Castle castle = CastleManager.getInstance().getCastleById(castleId);
final L2Clan owner = ClanTable.getInstance().getClan(castle.getOwnerId());
String content = HtmCache.getInstance().getHtm(CB_PATH + "region/castle.htm");
content = content.replace("%castleName%", castle.getName());
content = content.replace("%tax%", Integer.toString(castle.getTaxPercent()));
content = content.replace("%lord%", ((owner != null) ? owner.getLeaderName() : "None"));
content = content.replace("%clanName%", ((owner != null) ? "<a action=\"bypass _bbsclan;home;" + owner.getClanId() + "\">" + owner.getName() + "</a>" : "None"));
content = content.replace("%allyName%", (((owner != null) && (owner.getAllyId() > 0)) ? owner.getAllyName() : "None"));
content = content.replace("%siegeDate%", new SimpleDateFormat("yyyy-MM-dd HH:mm").format(castle.getSiegeDate().getTimeInMillis()));
final StringBuilder sb = new StringBuilder(200);
final List<ClanHall> clanHalls = ClanHallManager.getInstance().getClanHallsByLocation(castle.getName());
if ((clanHalls != null) && !clanHalls.isEmpty())
{
sb.append("<br><br><table width=610 bgcolor=A7A19A><tr><td width=5></td><td width=200>Clan Hall Name</td><td width=200>Owning Clan</td><td width=200>Clan Leader Name</td><td width=5></td></tr></table><br1>");
for (ClanHall ch : clanHalls)
{
final L2Clan chOwner = ClanTable.getInstance().getClan(ch.getOwnerId());
StringUtil.append(sb, "<table><tr><td width=5></td><td width=200>", ch.getName(), "</td><td width=200>", ((chOwner != null) ? "<a action=\"bypass _bbsclan;home;" + chOwner.getClanId() + "\">" + chOwner.getName() + "</a>" : "None"), "</td><td width=200>", ((chOwner != null) ? chOwner.getLeaderName() : "None"), "</td><td width=5></td></tr></table><br1><img src=\"L2UI.Squaregray\" width=605 height=1><br1>");
}
}
separateAndSend(content.replace("%hallsList%", sb.toString()), activeChar);
}
private static class SingletonHolder
{
protected static final RegionBBSManager _instance = new RegionBBSManager();
}
}

View File

@ -0,0 +1,64 @@
/*
* 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.gameserver.communitybbs.Manager;
import java.util.StringTokenizer;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
public class TopBBSManager extends BaseBBSManager
{
protected TopBBSManager()
{
}
public static TopBBSManager getInstance()
{
return SingletonHolder._instance;
}
@Override
public void parseCmd(String command, L2PcInstance activeChar)
{
if (command.equals("_bbshome"))
{
loadStaticHtm("index.htm", activeChar);
}
else if (command.startsWith("_bbshome;"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
loadStaticHtm(st.nextToken(), activeChar);
}
else
{
super.parseCmd(command, activeChar);
}
}
@Override
protected String getFolder()
{
return "top/";
}
private static class SingletonHolder
{
protected static final TopBBSManager _instance = new TopBBSManager();
}
}

View File

@ -0,0 +1,341 @@
/*
* 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.gameserver.communitybbs.Manager;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.concurrent.ConcurrentHashMap;
import com.l2jmobius.commons.util.StringUtil;
import com.l2jmobius.gameserver.communitybbs.CommunityBoard;
import com.l2jmobius.gameserver.communitybbs.BB.Forum;
import com.l2jmobius.gameserver.communitybbs.BB.Post;
import com.l2jmobius.gameserver.communitybbs.BB.Topic;
import com.l2jmobius.gameserver.datatables.sql.ClanTable;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
public class TopicBBSManager extends BaseBBSManager
{
private final List<Topic> _table;
private final Map<Forum, Integer> _maxId;
public static TopicBBSManager getInstance()
{
return SingletonHolder._instance;
}
protected TopicBBSManager()
{
_table = new ArrayList<>();
_maxId = new ConcurrentHashMap<>();
}
@Override
public void parseWrite(String ar1, String ar2, String ar3, String ar4, String ar5, L2PcInstance activeChar)
{
if (ar1.equals("crea"))
{
Forum f = ForumsBBSManager.getInstance().getForumByID(Integer.parseInt(ar2));
if (f == null)
{
separateAndSend("<html><body><br><br><center>The forum named '" + ar2 + "' doesn't exist.</center></body></html>", activeChar);
return;
}
f.vload();
Topic t = new Topic(Topic.ConstructorType.CREATE, TopicBBSManager.getInstance().getMaxID(f) + 1, Integer.parseInt(ar2), ar5, Calendar.getInstance().getTimeInMillis(), activeChar.getName(), activeChar.getObjectId(), Topic.MEMO, 0);
f.addTopic(t);
TopicBBSManager.getInstance().setMaxID(t.getID(), f);
Post p = new Post(activeChar.getName(), activeChar.getObjectId(), Calendar.getInstance().getTimeInMillis(), t.getID(), f.getID(), ar4);
PostBBSManager.getInstance().addPostByTopic(p, t);
parseCmd("_bbsmemo", activeChar);
}
else if (ar1.equals("del"))
{
Forum f = ForumsBBSManager.getInstance().getForumByID(Integer.parseInt(ar2));
if (f == null)
{
separateAndSend("<html><body><br><br><center>The forum named '" + ar2 + "' doesn't exist.</center></body></html>", activeChar);
return;
}
Topic t = f.getTopic(Integer.parseInt(ar3));
if (t == null)
{
separateAndSend("<html><body><br><br><center>The topic named '" + ar3 + "' doesn't exist.</center></body></html>", activeChar);
return;
}
Post p = PostBBSManager.getInstance().getPostByTopic(t);
if (p != null)
{
p.deleteMe(t);
}
t.deleteMe(f);
parseCmd("_bbsmemo", activeChar);
}
else
{
super.parseWrite(ar1, ar2, ar3, ar4, ar5, activeChar);
}
}
@Override
public void parseCmd(String command, L2PcInstance activeChar)
{
if (command.equals("_bbsmemo"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Memo Command", command);
showTopics(activeChar.getMemo(), activeChar, 1, activeChar.getMemo().getID());
}
else if (command.startsWith("_bbstopics;read"))
{
CommunityBoard.getInstance().addBypass(activeChar, "Topics Command", command);
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
String index = null;
if (st.hasMoreTokens())
{
index = st.nextToken();
}
int ind = 0;
if (index == null)
{
ind = 1;
}
else
{
ind = Integer.parseInt(index);
}
showTopics(ForumsBBSManager.getInstance().getForumByID(idf), activeChar, ind, idf);
}
else if (command.startsWith("_bbstopics;crea"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
showNewTopic(ForumsBBSManager.getInstance().getForumByID(idf), activeChar, idf);
}
else if (command.startsWith("_bbstopics;del"))
{
StringTokenizer st = new StringTokenizer(command, ";");
st.nextToken();
st.nextToken();
int idf = Integer.parseInt(st.nextToken());
int idt = Integer.parseInt(st.nextToken());
Forum f = ForumsBBSManager.getInstance().getForumByID(idf);
if (f == null)
{
separateAndSend("<html><body><br><br><center>The forum named '" + idf + "' doesn't exist.</center></body></html>", activeChar);
return;
}
Topic t = f.getTopic(idt);
if (t == null)
{
separateAndSend("<html><body><br><br><center>The topic named '" + idt + "' doesn't exist.</center></body></html>", activeChar);
return;
}
Post p = PostBBSManager.getInstance().getPostByTopic(t);
if (p != null)
{
p.deleteMe(t);
}
t.deleteMe(f);
parseCmd("_bbsmemo", activeChar);
}
else
{
super.parseCmd(command, activeChar);
}
}
public void addTopic(Topic tt)
{
_table.add(tt);
}
public void delTopic(Topic topic)
{
_table.remove(topic);
}
public void setMaxID(int id, Forum f)
{
_maxId.put(f, id);
}
public int getMaxID(Forum f)
{
Integer i = _maxId.get(f);
if (i == null)
{
return 0;
}
return i;
}
public Topic getTopicByID(int idf)
{
for (Topic t : _table)
{
if (t.getID() == idf)
{
return t;
}
}
return null;
}
private static void showNewTopic(Forum forum, L2PcInstance activeChar, int idf)
{
if (forum == null)
{
separateAndSend("<html><body><br><br><center>The forum named '" + idf + "' doesn't exist.</center></body></html>", activeChar);
return;
}
if (forum.getType() == Forum.MEMO)
{
showMemoNewTopics(forum, activeChar);
}
else
{
separateAndSend("<html><body><br><br><center>The forum named '" + forum.getName() + "' doesn't exist.</center></body></html>", activeChar);
}
}
private static void showMemoNewTopics(Forum forum, L2PcInstance activeChar)
{
final String html = "<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=0><tr><td width=610><img src=\"sek.cbui355\" width=\"610\" height=\"1\"><br1><img src=\"sek.cbui355\" width=\"610\" height=\"1\"></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=20></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&$413;</td><td FIXWIDTH=540><edit var = \"Title\" width=540 height=13></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29 valign=top>&$427;</td><td align=center FIXWIDTH=540><MultiEdit var =\"Content\" width=535 height=313></td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr></table><table fixwidth=610 border=0 cellspacing=0 cellpadding=0><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=1></td><td align=center FIXWIDTH=60 height=29>&nbsp;</td><td align=center FIXWIDTH=70><button value=\"&$140;\" action=\"Write Topic crea " + forum.getID() + " Title Content Title\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td><td align=center FIXWIDTH=70><button value = \"&$141;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"> </td><td align=center FIXWIDTH=400>&nbsp;</td><td><img src=\"l2ui.mini_logo\" width=5 height=1></td></tr></table></center></body></html>";
send1001(html, activeChar);
send1002(activeChar);
}
private void showTopics(Forum forum, L2PcInstance activeChar, int index, int idf)
{
if (forum == null)
{
separateAndSend("<html><body><br><br><center>The forum named '" + idf + "' doesn't exist.</center></body></html>", activeChar);
return;
}
if (forum.getType() == Forum.MEMO)
{
showMemoTopics(forum, activeChar, index);
}
else
{
separateAndSend("<html><body><br><br><center>The forum named '" + forum.getName() + "' doesn't exist.</center></body></html>", activeChar);
}
}
private void showMemoTopics(Forum forum, L2PcInstance activeChar, int index)
{
forum.vload();
final StringBuilder sb = new StringBuilder("<html><body><br><br><table border=0 width=610><tr><td width=10></td><td width=600 align=left><a action=\"bypass _bbshome\">HOME</a>&nbsp;>&nbsp;<a action=\"bypass _bbsmemo\">Memo Form</a></td></tr></table><img src=\"L2UI.squareblank\" width=\"1\" height=\"10\"><center><table border=0 cellspacing=0 cellpadding=2 bgcolor=888888 width=610><tr><td FIXWIDTH=5></td><td FIXWIDTH=415 align=center>&$413;</td><td FIXWIDTH=120 align=center></td><td FIXWIDTH=70 align=center>&$418;</td></tr></table>");
final DateFormat dateFormat = DateFormat.getInstance();
for (int i = 0, j = getMaxID(forum) + 1; i < (12 * index); j--)
{
if (j < 0)
{
break;
}
Topic t = forum.getTopic(j);
if (t != null)
{
if (i++ >= (12 * (index - 1)))
{
StringUtil.append(sb, "<table border=0 cellspacing=0 cellpadding=5 WIDTH=610><tr><td FIXWIDTH=5></td><td FIXWIDTH=415><a action=\"bypass _bbsposts;read;", forum.getID(), ";", t.getID(), "\">", t.getName(), "</a></td><td FIXWIDTH=120 align=center></td><td FIXWIDTH=70 align=center>", dateFormat.format(new Date(t.getDate())), "</td></tr></table><img src=\"L2UI.Squaregray\" width=\"610\" height=\"1\">");
}
}
}
sb.append("<br><table width=610 cellspace=0 cellpadding=0><tr><td width=50><button value=\"&$422;\" action=\"bypass _bbsmemo\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td><td width=510 align=center><table border=0><tr>");
if (index == 1)
{
sb.append("<td><button action=\"\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16 ></td>");
}
else
{
StringUtil.append(sb, "<td><button action=\"bypass _bbstopics;read;", forum.getID(), ";", index - 1, "\" back=\"l2ui_ch3.prev1_down\" fore=\"l2ui_ch3.prev1\" width=16 height=16 ></td>");
}
int nbp;
nbp = forum.getTopicSize() / 8;
if ((nbp * 8) != ClanTable.getInstance().getClans().length)
{
nbp++;
}
for (int i = 1; i <= nbp; i++)
{
if (i == index)
{
StringUtil.append(sb, "<td> ", i, " </td>");
}
else
{
StringUtil.append(sb, "<td><a action=\"bypass _bbstopics;read;", forum.getID(), ";", i, "\"> ", i, " </a></td>");
}
}
if (index == nbp)
{
sb.append("<td><button action=\"\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16 ></td>");
}
else
{
StringUtil.append(sb, "<td><button action=\"bypass _bbstopics;read;", forum.getID(), ";", index + 1, "\" back=\"l2ui_ch3.next1_down\" fore=\"l2ui_ch3.next1\" width=16 height=16 ></td>");
}
StringUtil.append(sb, "</tr></table></td><td align=right><button value = \"&$421;\" action=\"bypass _bbstopics;crea;", forum.getID(), "\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\" ></td></tr><tr><td><img src=\"l2ui.mini_logo\" width=5 height=10></td></tr><tr><td></td><td align=center><table border=0><tr><td></td><td><edit var = \"Search\" width=130 height=11></td><td><button value=\"&$420;\" action=\"Write 5 -2 0 Search _ _\" back=\"l2ui_ch3.smallbutton2_down\" width=65 height=20 fore=\"l2ui_ch3.smallbutton2\"></td></tr></table></td></tr></table><br><br><br></center></body></html>");
separateAndSend(sb.toString(), activeChar);
}
private static class SingletonHolder
{
protected static final TopicBBSManager _instance = new TopicBBSManager();
}
}

View File

@ -0,0 +1,248 @@
/*
* 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.gameserver.datatables;
/**
* @author FBIagent<br>
*/
public class AccessLevel
{
/**
* The logger<br>
*/
// private final static Log LOGGER = LogFactory.getLog(AccessLevel.class);
/**
* The access level<br>
*/
private int _accessLevel = 0;
/**
* The access level name<br>
*/
private String _name = null;
/**
* The name color for the access level<br>
*/
private int _nameColor = 0;
/**
* The title color for the access level<br>
*/
private int _titleColor = 0;
/**
* Flag to determine if the access level has gm access<br>
*/
private boolean _isGm = false;
/** Flag for peace zone attack */
private boolean _allowPeaceAttack = false;
/** Flag for fixed res */
private boolean _allowFixedRes = false;
/** Flag for transactions */
private boolean _allowTransaction = false;
/** Flag for AltG commands */
private boolean _allowAltG = false;
/** Flag to give damage */
private boolean _giveDamage = false;
/** Flag to take aggro */
private boolean _takeAggro = false;
/** Flag to gain exp in party */
private boolean _gainExp = false;
// L2EMU_ ADD - Rayan
private boolean _useNameColor = true;
private boolean _useTitleColor = false;
private boolean _canDisableGmStatus = false;
// L2EMU_ ADD
/**
* Initializes members<br>
* <br>
* @param accessLevel as int<br>
* @param name as String<br>
* @param nameColor as int<br>
* @param titleColor as int<br>
* @param isGm as boolean<br>
* @param allowPeaceAttack as boolean<br>
* @param allowFixedRes as boolean<br>
* @param allowTransaction as boolean<br>
* @param allowAltG as boolean<br>
* @param giveDamage as boolean<br>
* @param takeAggro as boolean<br>
* @param gainExp as boolean<br>
* @param useNameColor as boolean<br>
* @param useTitleColor as boolean<br>
* @param canDisableGmStatus
*/
public AccessLevel(int accessLevel, String name, int nameColor, int titleColor, boolean isGm, boolean allowPeaceAttack, boolean allowFixedRes, boolean allowTransaction, boolean allowAltG, boolean giveDamage, boolean takeAggro, boolean gainExp, boolean useNameColor, boolean useTitleColor, boolean canDisableGmStatus)
{
_accessLevel = accessLevel;
_name = name;
_nameColor = nameColor;
_titleColor = titleColor;
_isGm = isGm;
_allowPeaceAttack = allowPeaceAttack;
_allowFixedRes = allowFixedRes;
_allowTransaction = allowTransaction;
_allowAltG = allowAltG;
_giveDamage = giveDamage;
_takeAggro = takeAggro;
_gainExp = gainExp;
// L2EMU_ ADD - Rayan
_useNameColor = useNameColor;
_useTitleColor = useTitleColor;
_canDisableGmStatus = canDisableGmStatus;
// L2EMU_ ADD
}
/**
* Returns the access level<br>
* <br>
* @return int: access level<br>
*/
public int getLevel()
{
return _accessLevel;
}
/**
* Returns the access level name<br>
* <br>
* @return String: access level name<br>
*/
public String getName()
{
return _name;
}
/**
* Returns the name color of the access level<br>
* <br>
* @return int: the name color for the access level<br>
*/
public int getNameColor()
{
return _nameColor;
}
/**
* Returns the title color color of the access level<br>
* <br>
* @return int: the title color for the access level<br>
*/
public int getTitleColor()
{
return _titleColor;
}
/**
* Retuns if the access level has gm access or not<br>
* <br>
* @return boolean: true if access level have gm access, otherwise false<br>
*/
public boolean isGm()
{
return _isGm;
}
/**
* Returns if the access level is allowed to attack in peace zone or not<br>
* <br>
* @return boolean: true if the access level is allowed to attack in peace zone, otherwise false<br>
*/
public boolean allowPeaceAttack()
{
return _allowPeaceAttack;
}
/**
* @return true if the access level is allowed to use fixed res, otherwise false.
*/
public boolean allowFixedRes()
{
return _allowFixedRes;
}
/**
* Returns if the access level is allowed to perform transactions or not<br>
* <br>
* @return boolean: true if access level is allowed to perform transactions, otherwise false<br>
*/
public boolean allowTransaction()
{
return _allowTransaction;
}
/**
* Returns if the access level is allowed to use AltG commands or not<br>
* <br>
* @return boolean: true if access level is allowed to use AltG commands, otherwise false<br>
*/
public boolean allowAltG()
{
return _allowAltG;
}
/**
* Returns if the access level can give damage or not<br>
* <br>
* @return boolean: true if the access level can give damage, otherwise false<br>
*/
public boolean canGiveDamage()
{
return _giveDamage;
}
/**
* Returns if the access level can take aggro or not<br>
* <br>
* @return boolean: true if the access level can take aggro, otherwise false<br>
*/
public boolean canTakeAggro()
{
return _takeAggro;
}
/**
* Returns if the access level can gain exp or not<br>
* <br>
* @return boolean: true if the access level can gain exp, otherwise false<br>
*/
public boolean canGainExp()
{
return _gainExp;
}
// L2EMU_ADD - Rayan
public boolean useNameColor()
{
return _useNameColor;
}
public boolean useTitleColor()
{
return _useTitleColor;
}
/**
* Retuns if the access level is a gm that can temp disable gm access<br>
* <br>
* @return boolean: true if is a gm that can temp disable gm access, otherwise false<br>
*/
public boolean canDisableGmStatus()
{
return _canDisableGmStatus;
}
}

View File

@ -0,0 +1,290 @@
/*
* 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.gameserver.datatables;
import java.io.File;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.commons.util.StringUtil;
import com.l2jmobius.gameserver.model.holders.BuffSkillHolder;
/**
* This class loads available skills and stores players' buff schemes into _schemesTable.
*/
public class BufferTable
{
private static final Logger LOGGER = Logger.getLogger(BufferTable.class.getName());
private static final String LOAD_SCHEMES = "SELECT * FROM buffer_schemes";
private static final String DELETE_SCHEMES = "TRUNCATE TABLE buffer_schemes";
private static final String INSERT_SCHEME = "INSERT INTO buffer_schemes (object_id, scheme_name, skills) VALUES (?,?,?)";
private final Map<Integer, HashMap<String, ArrayList<Integer>>> _schemesTable = new ConcurrentHashMap<>();
private final Map<Integer, BuffSkillHolder> _availableBuffs = new LinkedHashMap<>();
public BufferTable()
{
int count = 0;
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement st = con.prepareStatement(LOAD_SCHEMES);
ResultSet rs = st.executeQuery();
while (rs.next())
{
final int objectId = rs.getInt("object_id");
final String schemeName = rs.getString("scheme_name");
final String[] skills = rs.getString("skills").split(",");
ArrayList<Integer> schemeList = new ArrayList<>();
for (String skill : skills)
{
// Don't feed the skills list if the list is empty.
if (skill.isEmpty())
{
break;
}
schemeList.add(Integer.valueOf(skill));
}
setScheme(objectId, schemeName, schemeList);
count++;
}
rs.close();
st.close();
}
catch (Exception e)
{
LOGGER.warning("BufferTable: Failed to load buff schemes : " + e);
}
try
{
DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
DocumentBuilder db = dbf.newDocumentBuilder();
Document doc = db.parse(new File("./data/buffer_skills.xml"));
final Node n = doc.getFirstChild();
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if (!d.getNodeName().equalsIgnoreCase("category"))
{
continue;
}
final String category = d.getAttributes().getNamedItem("type").getNodeValue();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if (!c.getNodeName().equalsIgnoreCase("buff"))
{
continue;
}
final NamedNodeMap attrs = c.getAttributes();
final int skillId = Integer.parseInt(attrs.getNamedItem("id").getNodeValue());
_availableBuffs.put(skillId, new BuffSkillHolder(skillId, Integer.parseInt(attrs.getNamedItem("price").getNodeValue()), category, attrs.getNamedItem("desc").getNodeValue()));
}
}
}
catch (Exception e)
{
LOGGER.warning("BufferTable: Failed to load buff info : " + e);
}
LOGGER.info("BufferTable: Loaded " + count + " players schemes and " + _availableBuffs.size() + " available buffs.");
}
public void saveSchemes()
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
// Delete all entries from database.
PreparedStatement st = con.prepareStatement(DELETE_SCHEMES);
st.execute();
st.close();
st = con.prepareStatement(INSERT_SCHEME);
// Save _schemesTable content.
for (Map.Entry<Integer, HashMap<String, ArrayList<Integer>>> player : _schemesTable.entrySet())
{
for (Map.Entry<String, ArrayList<Integer>> scheme : player.getValue().entrySet())
{
// Build a String composed of skill ids seperated by a ",".
final StringBuilder sb = new StringBuilder();
for (int skillId : scheme.getValue())
{
StringUtil.append(sb, skillId, ",");
}
// Delete the last "," : must be called only if there is something to delete !
if (sb.length() > 0)
{
sb.setLength(sb.length() - 1);
}
st.setInt(1, player.getKey());
st.setString(2, scheme.getKey());
st.setString(3, sb.toString());
st.addBatch();
}
}
st.executeBatch();
st.close();
}
catch (Exception e)
{
LOGGER.warning("BufferTable: Error while saving schemes : " + e);
}
}
public void setScheme(int playerId, String schemeName, ArrayList<Integer> list)
{
if (!_schemesTable.containsKey(playerId))
{
_schemesTable.put(playerId, new HashMap<String, ArrayList<Integer>>());
}
else if (_schemesTable.get(playerId).size() >= Config.BUFFER_MAX_SCHEMES)
{
return;
}
_schemesTable.get(playerId).put(schemeName, list);
}
/**
* @param playerId : The player objectId to check.
* @return the list of schemes for a given player.
*/
public Map<String, ArrayList<Integer>> getPlayerSchemes(int playerId)
{
return _schemesTable.get(playerId);
}
/**
* @param playerId : The player objectId to check.
* @param schemeName : The scheme name to check.
* @return the List holding skills for the given scheme name and player, or null (if scheme or player isn't registered).
*/
public List<Integer> getScheme(int playerId, String schemeName)
{
if ((_schemesTable.get(playerId) == null) || (_schemesTable.get(playerId).get(schemeName) == null))
{
return Collections.emptyList();
}
return _schemesTable.get(playerId).get(schemeName);
}
/**
* @param playerId : The player objectId to check.
* @param schemeName : The scheme name to check.
* @param skillId : The skill id to check.
* @return true if the skill is already registered on the scheme, or false otherwise.
*/
public boolean getSchemeContainsSkill(int playerId, String schemeName, int skillId)
{
final List<Integer> skills = getScheme(playerId, schemeName);
if (skills.isEmpty())
{
return false;
}
for (int id : skills)
{
if (id == skillId)
{
return true;
}
}
return false;
}
/**
* @param groupType : The type of skills to return.
* @return a list of skills ids based on the given groupType.
*/
public List<Integer> getSkillsIdsByType(String groupType)
{
List<Integer> skills = new ArrayList<>();
for (BuffSkillHolder skill : _availableBuffs.values())
{
if (skill.getType().equalsIgnoreCase(groupType))
{
skills.add(skill.getId());
}
}
return skills;
}
/**
* @return a list of all buff types available.
*/
public List<String> getSkillTypes()
{
List<String> skillTypes = new ArrayList<>();
for (BuffSkillHolder skill : _availableBuffs.values())
{
if (!skillTypes.contains(skill.getType()))
{
skillTypes.add(skill.getType());
}
}
return skillTypes;
}
public BuffSkillHolder getAvailableBuff(int skillId)
{
return _availableBuffs.get(skillId);
}
public static BufferTable getInstance()
{
return SingletonHolder.INSTANCE;
}
private static class SingletonHolder
{
protected static final BufferTable INSTANCE = new BufferTable();
}
}

View File

@ -0,0 +1,116 @@
/*
* 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.gameserver.datatables;
import java.util.ArrayList;
import java.util.List;
/**
* This class has just one simple function to return the item id of a crown regarding to castleid
* @author evill33t
*/
public class CrownTable
{
private static List<Integer> _crownList = new ArrayList<>();
public static List<Integer> getCrownList()
{
if (_crownList.isEmpty())
{
_crownList.add(6841); // Crown of the lord
_crownList.add(6834); // Innadril
_crownList.add(6835); // Dion
_crownList.add(6836); // Goddard
_crownList.add(6837); // Oren
_crownList.add(6838); // Gludio
_crownList.add(6839); // Giran
_crownList.add(6840); // Aden
_crownList.add(8182); // Rune
_crownList.add(8183); // Schuttgart
}
return _crownList;
}
public static int getCrownId(int CastleId)
{
int CrownId = 0;
switch (CastleId)
{
// Gludio
case 1:
{
CrownId = 6838;
break;
}
// Dion
case 2:
{
CrownId = 6835;
break;
}
// Giran
case 3:
{
CrownId = 6839;
break;
}
// Oren
case 4:
{
CrownId = 6837;
break;
}
// Aden
case 5:
{
CrownId = 6840;
break;
}
// Innadril
case 6:
{
CrownId = 6834;
break;
}
// Goddard
case 7:
{
CrownId = 6836;
break;
}
// Rune
case 8:
{
CrownId = 8182;
break;
}
// Schuttgart
case 9:
{
CrownId = 8183;
break;
}
default:
{
CrownId = 0;
break;
}
}
return CrownId;
}
}

View File

@ -0,0 +1,200 @@
/*
* 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.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import com.l2jmobius.gameserver.model.L2Object;
/**
* This class ...
* @version $Revision$ $Date$
*/
public class DesireTable
{
public static final DesireType[] DEFAULT_DESIRES =
{
DesireType.FEAR,
DesireType.DISLIKE,
DesireType.HATE,
DesireType.DAMAGE
};
public enum DesireType
{
FEAR,
DISLIKE,
HATE,
DAMAGE
}
class DesireValue
{
private float _value;
DesireValue()
{
this(0f);
}
DesireValue(Float pValue)
{
_value = pValue;
}
public void addValue(float pValue)
{
_value += pValue;
}
public float getValue()
{
return _value;
}
}
class Desires
{
private final Map<DesireType, DesireValue> _desireTable;
public Desires(DesireType... desireList)
{
_desireTable = new HashMap<>();
for (DesireType desire : desireList)
{
_desireTable.put(desire, new DesireValue());
}
}
public DesireValue getDesireValue(DesireType type)
{
return _desireTable.get(type);
}
public void addValue(DesireType type, float value)
{
DesireValue temp = getDesireValue(type);
if (temp != null)
{
temp.addValue(value);
}
}
public void createDesire(DesireType type)
{
_desireTable.put(type, new DesireValue());
}
public void deleteDesire(DesireType type)
{
_desireTable.remove(type);
}
}
private final Map<L2Object, Desires> _objectDesireTable;
private final Desires _generalDesires;
private final DesireType[] _desireTypes;
public DesireTable(DesireType... desireList)
{
_desireTypes = desireList;
_objectDesireTable = new HashMap<>();
_generalDesires = new Desires(_desireTypes);
}
public float getDesireValue(DesireType type)
{
return _generalDesires.getDesireValue(type).getValue();
}
public float getDesireValue(L2Object object, DesireType type)
{
final Desires desireList = _objectDesireTable.get(object);
if (desireList == null)
{
return 0f;
}
return desireList.getDesireValue(type).getValue();
}
public void addDesireValue(DesireType type, float value)
{
_generalDesires.addValue(type, value);
}
public void addDesireValue(L2Object object, DesireType type, float value)
{
Desires desireList = _objectDesireTable.get(object);
if (desireList != null)
{
desireList.addValue(type, value);
}
}
public void createDesire(DesireType type)
{
_generalDesires.createDesire(type);
}
public void deleteDesire(DesireType type)
{
_generalDesires.deleteDesire(type);
}
public void createDesire(L2Object object, DesireType type)
{
Desires desireList = _objectDesireTable.get(object);
if (desireList != null)
{
desireList.createDesire(type);
}
}
public void deleteDesire(L2Object object, DesireType type)
{
Desires desireList = _objectDesireTable.get(object);
if (desireList != null)
{
desireList.deleteDesire(type);
}
}
public void addKnownObject(L2Object object)
{
if (object != null)
{
addKnownObject(object, DesireType.DISLIKE, DesireType.FEAR, DesireType.DAMAGE, DesireType.HATE);
}
}
public void addKnownObject(L2Object object, DesireType... desireList)
{
if (object != null)
{
_objectDesireTable.put(object, new Desires(desireList));
}
}
}

View File

@ -0,0 +1,200 @@
/*
* 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.gameserver.datatables;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.SystemMessageId;
import com.l2jmobius.gameserver.network.serverpackets.L2GameServerPacket;
import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
/**
* This class stores references to all online game masters. (access level > 100)
* @version $Revision: 1.2.2.1.2.7 $ $Date: 2005/04/05 19:41:24 $
*/
public class GmListTable
{
protected static final Logger LOGGER = Logger.getLogger(GmListTable.class.getName());
private static GmListTable _instance;
/** Set(L2PcInstance>) containing all the GM in game */
private final Map<L2PcInstance, Boolean> _gmList;
public static GmListTable getInstance()
{
if (_instance == null)
{
_instance = new GmListTable();
}
return _instance;
}
public static void reload()
{
_instance = null;
getInstance();
}
public List<L2PcInstance> getAllGms(boolean includeHidden)
{
final List<L2PcInstance> tmpGmList = new ArrayList<>();
for (Entry<L2PcInstance, Boolean> n : _gmList.entrySet())
{
if (includeHidden || !n.getValue())
{
tmpGmList.add(n.getKey());
}
}
return tmpGmList;
}
public List<String> getAllGmNames(boolean includeHidden)
{
final List<String> tmpGmList = new ArrayList<>();
for (Entry<L2PcInstance, Boolean> n : _gmList.entrySet())
{
if (!n.getValue())
{
tmpGmList.add(n.getKey().getName());
}
else if (includeHidden)
{
tmpGmList.add(n.getKey().getName() + " (invis)");
}
}
return tmpGmList;
}
private GmListTable()
{
LOGGER.info("GmListTable: initalized.");
_gmList = new ConcurrentHashMap<>();
}
/**
* Add a L2PcInstance player to the Set _gmList
* @param player
* @param hidden
*/
public void addGm(L2PcInstance player, boolean hidden)
{
if (Config.DEBUG)
{
LOGGER.info("added gm: " + player.getName());
}
_gmList.put(player, hidden);
}
public void deleteGm(L2PcInstance player)
{
if (Config.DEBUG)
{
LOGGER.info("deleted gm: " + player.getName());
}
_gmList.remove(player);
}
/**
* GM will be displayed on clients GM list.
* @param player the player
*/
public void showGm(L2PcInstance player)
{
if (_gmList.containsKey(player))
{
_gmList.put(player, false);
}
}
/**
* GM will no longer be displayed on clients GM list.
* @param player the player
*/
public void hideGm(L2PcInstance player)
{
if (_gmList.containsKey(player))
{
_gmList.put(player, true);
}
}
public boolean isGmOnline(boolean includeHidden)
{
for (boolean b : _gmList.values())
{
if (includeHidden || !b)
{
return true;
}
}
return false;
}
public void sendListToPlayer(L2PcInstance player)
{
if (isGmOnline(player.isGM()))
{
SystemMessage sm = new SystemMessage(SystemMessageId.GM_LIST);
player.sendPacket(sm);
for (String name : getAllGmNames(player.isGM()))
{
final SystemMessage sm1 = new SystemMessage(SystemMessageId.GM_S1);
sm1.addString(name);
player.sendPacket(sm1);
}
}
else
{
SystemMessage sm2 = new SystemMessage(SystemMessageId.NO_GM_PROVIDING_SERVICE_NOW);
player.sendPacket(sm2);
}
}
public static void broadcastToGMs(L2GameServerPacket packet)
{
for (L2PcInstance gm : getInstance().getAllGms(true))
{
gm.sendPacket(packet);
}
}
public static void broadcastMessageToGMs(String message)
{
for (L2PcInstance gm : getInstance().getAllGms(true))
{
// prevents a NPE.
if (gm != null)
{
gm.sendPacket(SystemMessage.sendString(message));
}
}
}
}

View File

@ -0,0 +1,75 @@
/*
* 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.gameserver.datatables;
import com.l2jmobius.gameserver.model.L2Skill;
/**
* @author BiTi
*/
public class HeroSkillTable
{
private static HeroSkillTable _instance;
private static L2Skill[] _heroSkills;
private HeroSkillTable()
{
_heroSkills = new L2Skill[5];
_heroSkills[0] = SkillTable.getInstance().getInfo(395, 1);
_heroSkills[1] = SkillTable.getInstance().getInfo(396, 1);
_heroSkills[2] = SkillTable.getInstance().getInfo(1374, 1);
_heroSkills[3] = SkillTable.getInstance().getInfo(1375, 1);
_heroSkills[4] = SkillTable.getInstance().getInfo(1376, 1);
}
public static HeroSkillTable getInstance()
{
if (_instance == null)
{
_instance = new HeroSkillTable();
}
return _instance;
}
public static L2Skill[] getHeroSkills()
{
return _heroSkills;
}
public static boolean isHeroSkill(int skillid)
{
Integer[] _HeroSkillsId = new Integer[]
{
395,
396,
1374,
1375,
1376
};
for (int id : _HeroSkillsId)
{
if (id == skillid)
{
return true;
}
}
return false;
}
}

View File

@ -0,0 +1,88 @@
/*
* This file is part of the L2J Mobius project.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import com.l2jmobius.gameserver.model.MobGroup;
import com.l2jmobius.gameserver.model.actor.instance.L2ControllableMobInstance;
/**
* @author littlecrow
*/
public class MobGroupTable
{
private static MobGroupTable _instance;
private final Map<Integer, MobGroup> _groupMap;
public static final int FOLLOW_RANGE = 300;
public static final int RANDOM_RANGE = 300;
public MobGroupTable()
{
_groupMap = new HashMap<>();
}
public static MobGroupTable getInstance()
{
if (_instance == null)
{
_instance = new MobGroupTable();
}
return _instance;
}
public void addGroup(int groupKey, MobGroup group)
{
_groupMap.put(groupKey, group);
}
public MobGroup getGroup(int groupKey)
{
return _groupMap.get(groupKey);
}
public int getGroupCount()
{
return _groupMap.size();
}
public MobGroup getGroupForMob(L2ControllableMobInstance mobInst)
{
for (MobGroup mobGroup : _groupMap.values())
{
if (mobGroup.isGroupMember(mobInst))
{
return mobGroup;
}
}
return null;
}
public MobGroup[] getGroups()
{
return _groupMap.values().toArray(new MobGroup[getGroupCount()]);
}
public boolean removeGroup(int groupKey)
{
return _groupMap.remove(groupKey) != null;
}
}

View File

@ -0,0 +1,56 @@
/*
* 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.gameserver.datatables;
import com.l2jmobius.gameserver.model.L2Skill;
/**
* @author -Nemesiss-
*/
public class NobleSkillTable
{
private static NobleSkillTable _instance;
private static L2Skill[] _nobleSkills;
private NobleSkillTable()
{
_nobleSkills = new L2Skill[8];
_nobleSkills[0] = SkillTable.getInstance().getInfo(1323, 1);
_nobleSkills[1] = SkillTable.getInstance().getInfo(325, 1);
_nobleSkills[2] = SkillTable.getInstance().getInfo(326, 1);
_nobleSkills[3] = SkillTable.getInstance().getInfo(327, 1);
_nobleSkills[4] = SkillTable.getInstance().getInfo(1324, 1);
_nobleSkills[5] = SkillTable.getInstance().getInfo(1325, 1);
_nobleSkills[6] = SkillTable.getInstance().getInfo(1326, 1);
_nobleSkills[7] = SkillTable.getInstance().getInfo(1327, 1);
}
public static NobleSkillTable getInstance()
{
if (_instance == null)
{
_instance = new NobleSkillTable();
}
return _instance;
}
public L2Skill[] GetNobleSkills()
{
return _nobleSkills;
}
}

View File

@ -0,0 +1,422 @@
/*
* 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.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Calendar;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.model.L2ManufactureItem;
import com.l2jmobius.gameserver.model.L2ManufactureList;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.TradeList.TradeItem;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.network.L2GameClient;
import com.l2jmobius.gameserver.network.L2GameClient.GameClientState;
import com.l2jmobius.gameserver.thread.LoginServerThread;
/**
* @author Shyla
*/
public class OfflineTradeTable
{
private static Logger LOGGER = Logger.getLogger(OfflineTradeTable.class.getName());
// SQL DEFINITIONS
private static final String SAVE_OFFLINE_STATUS = "INSERT INTO character_offline_trade (`charId`,`time`,`type`,`title`) VALUES (?,?,?,?)";
private static final String SAVE_ITEMS = "INSERT INTO character_offline_trade_items (`charId`,`item`,`count`,`price`,`enchant`) VALUES (?,?,?,?,?)";
private static final String DELETE_OFFLINE_TABLE_ALL_ITEMS = "delete from character_offline_trade_items where charId=?";
private static final String DELETE_OFFLINE_TRADER = "DELETE FROM character_offline_trade where charId=?";
private static final String CLEAR_OFFLINE_TABLE = "DELETE FROM character_offline_trade";
private static final String CLEAR_OFFLINE_TABLE_ITEMS = "DELETE FROM character_offline_trade_items";
private static final String LOAD_OFFLINE_STATUS = "SELECT * FROM character_offline_trade";
private static final String LOAD_OFFLINE_ITEMS = "SELECT * FROM character_offline_trade_items WHERE charId = ?";
// called when server will go off, different from storeOffliner because
// of store of normal sellers/buyers also if not in offline mode
public static void storeOffliners()
{
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement stm = con.prepareStatement(CLEAR_OFFLINE_TABLE);
stm.execute();
stm.close();
stm = con.prepareStatement(CLEAR_OFFLINE_TABLE_ITEMS);
stm.execute();
stm.close();
con.setAutoCommit(false); // avoid halfway done
stm = con.prepareStatement(SAVE_OFFLINE_STATUS);
final PreparedStatement stm_items = con.prepareStatement(SAVE_ITEMS);
for (L2PcInstance pc : L2World.getInstance().getAllPlayers())
{
try
{
// without second check, server will store all guys that are in shop mode
if ((pc.getPrivateStoreType() != L2PcInstance.STORE_PRIVATE_NONE)/* && (pc.isOffline()) */)
{
stm.setInt(1, pc.getObjectId()); // Char Id
stm.setLong(2, pc.getOfflineStartTime());
stm.setInt(3, pc.getPrivateStoreType()); // store type
String title = null;
switch (pc.getPrivateStoreType())
{
case L2PcInstance.STORE_PRIVATE_BUY:
{
if (!Config.OFFLINE_TRADE_ENABLE)
{
continue;
}
title = pc.getBuyList().getTitle();
for (TradeItem i : pc.getBuyList().getItems())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getItem().getItemId());
stm_items.setLong(3, i.getCount());
stm_items.setLong(4, i.getPrice());
stm_items.setLong(5, i.getEnchant());
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
}
case L2PcInstance.STORE_PRIVATE_SELL:
case L2PcInstance.STORE_PRIVATE_PACKAGE_SELL:
{
if (!Config.OFFLINE_TRADE_ENABLE)
{
continue;
}
title = pc.getSellList().getTitle();
pc.getSellList().updateItems();
for (TradeItem i : pc.getSellList().getItems())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getObjectId());
stm_items.setLong(3, i.getCount());
stm_items.setLong(4, i.getPrice());
stm_items.setLong(5, i.getEnchant());
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
}
case L2PcInstance.STORE_PRIVATE_MANUFACTURE:
{
if (!Config.OFFLINE_CRAFT_ENABLE)
{
continue;
}
title = pc.getCreateList().getStoreName();
for (L2ManufactureItem i : pc.getCreateList().getList())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getRecipeId());
stm_items.setLong(3, 0);
stm_items.setLong(4, i.getCost());
stm_items.setLong(5, 0);
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
}
default:
{
// LOGGER.info( "OfflineTradersTable[storeTradeItems()]: Error while saving offline trader: " + pc.getObjectId() + ", store type: "+pc.getPrivateStoreType());
// no save for this kind of shop
continue;
}
}
stm.setString(4, title);
stm.executeUpdate();
stm.clearParameters();
con.commit(); // flush
}
}
catch (Exception e)
{
LOGGER.warning("OfflineTradersTable[storeTradeItems()]: Error while saving offline trader: " + pc.getObjectId() + " " + e);
}
}
stm.close();
stm_items.close();
LOGGER.info("Offline traders stored.");
}
catch (Exception e)
{
LOGGER.warning("OfflineTradersTable[storeTradeItems()]: Error while saving offline traders: " + e);
}
}
public static void restoreOfflineTraders()
{
LOGGER.info("Loading offline traders...");
int nTraders = 0;
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
final PreparedStatement stm = con.prepareStatement(LOAD_OFFLINE_STATUS);
final ResultSet rs = stm.executeQuery();
while (rs.next())
{
final long time = rs.getLong("time");
if (Config.OFFLINE_MAX_DAYS > 0)
{
final Calendar cal = Calendar.getInstance();
cal.setTimeInMillis(time);
cal.add(Calendar.DAY_OF_YEAR, Config.OFFLINE_MAX_DAYS);
if (cal.getTimeInMillis() <= System.currentTimeMillis())
{
LOGGER.info("Offline trader with id " + rs.getInt("charId") + " reached OfflineMaxDays, kicked.");
continue;
}
}
final int type = rs.getInt("type");
if (type == L2PcInstance.STORE_PRIVATE_NONE)
{
continue;
}
L2PcInstance player = null;
try
{
final L2GameClient client = new L2GameClient(null);
player = L2PcInstance.load(rs.getInt("charId"));
client.setActiveChar(player);
client.setAccountName(player.getAccountName());
client.setState(GameClientState.IN_GAME);
player.setClient(client);
player.setOfflineMode(true);
player.setOnlineStatus(false);
player.setOfflineStartTime(time);
if (Config.OFFLINE_SLEEP_EFFECT)
{
player.startAbnormalEffect(L2Character.ABNORMAL_EFFECT_SLEEP);
}
player.spawnMe(player.getX(), player.getY(), player.getZ());
LoginServerThread.getInstance().addGameServerLogin(player.getAccountName(), client);
final PreparedStatement stm_items = con.prepareStatement(LOAD_OFFLINE_ITEMS);
stm_items.setInt(1, player.getObjectId());
final ResultSet items = stm_items.executeQuery();
switch (type)
{
case L2PcInstance.STORE_PRIVATE_BUY:
{
while (items.next())
{
player.getBuyList().addItemByItemId(items.getInt(2), items.getInt(3), items.getInt(4), items.getInt(5));
}
player.getBuyList().setTitle(rs.getString("title"));
break;
}
case L2PcInstance.STORE_PRIVATE_SELL:
case L2PcInstance.STORE_PRIVATE_PACKAGE_SELL:
{
while (items.next())
{
player.getSellList().addItem(items.getInt(2), items.getInt(3), items.getInt(4));
}
player.getSellList().setTitle(rs.getString("title"));
player.getSellList().setPackaged(type == L2PcInstance.STORE_PRIVATE_PACKAGE_SELL);
break;
}
case L2PcInstance.STORE_PRIVATE_MANUFACTURE:
{
final L2ManufactureList createList = new L2ManufactureList();
while (items.next())
{
createList.add(new L2ManufactureItem(items.getInt(2), items.getInt(4)));
}
player.setCreateList(createList);
player.getCreateList().setStoreName(rs.getString("title"));
break;
}
default:
{
LOGGER.info("Offline trader " + player.getName() + " finished to sell his items");
}
}
items.close();
stm_items.close();
player.sitDown();
if (Config.OFFLINE_MODE_SET_INVULNERABLE)
{
player.setIsInvul(true);
}
if (Config.OFFLINE_SET_NAME_COLOR)
{
player._originalNameColorOffline = player.getAppearance().getNameColor();
player.getAppearance().setNameColor(Config.OFFLINE_NAME_COLOR);
}
player.setPrivateStoreType(type);
player.setOnlineStatus(true);
player.restoreEffects();
player.broadcastUserInfo();
nTraders++;
}
catch (Exception e)
{
LOGGER.warning("OfflineTradersTable[loadOffliners()]: Error loading trader: " + e);
if (player != null)
{
player.logout();
}
}
}
rs.close();
stm.close();
LOGGER.info("Loaded: " + nTraders + " offline trader(s)");
}
catch (Exception e)
{
LOGGER.warning("OfflineTradersTable[loadOffliners()]: Error while loading offline traders: " + e);
}
}
public static void storeOffliner(L2PcInstance pc)
{
if ((pc.getPrivateStoreType() == L2PcInstance.STORE_PRIVATE_NONE) || (!pc.isInOfflineMode()))
{
return;
}
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
PreparedStatement stm = con.prepareStatement(DELETE_OFFLINE_TABLE_ALL_ITEMS);
stm.setInt(1, pc.getObjectId());
stm.execute();
stm.clearParameters();
stm.close();
stm = con.prepareStatement(DELETE_OFFLINE_TRADER);
stm.setInt(1, pc.getObjectId());
stm.execute();
stm.clearParameters();
stm.close();
con.setAutoCommit(false); // avoid halfway done
stm = con.prepareStatement(SAVE_OFFLINE_STATUS);
final PreparedStatement stm_items = con.prepareStatement(SAVE_ITEMS);
boolean save = true;
try
{
stm.setInt(1, pc.getObjectId()); // Char Id
stm.setLong(2, pc.getOfflineStartTime());
stm.setInt(3, pc.getPrivateStoreType()); // store type
String title = null;
switch (pc.getPrivateStoreType())
{
case L2PcInstance.STORE_PRIVATE_BUY:
{
if (!Config.OFFLINE_TRADE_ENABLE)
{
break;
}
title = pc.getBuyList().getTitle();
for (TradeItem i : pc.getBuyList().getItems())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getItem().getItemId());
stm_items.setLong(3, i.getCount());
stm_items.setLong(4, i.getPrice());
stm_items.setLong(5, i.getEnchant());
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
}
case L2PcInstance.STORE_PRIVATE_SELL:
case L2PcInstance.STORE_PRIVATE_PACKAGE_SELL:
{
if (!Config.OFFLINE_TRADE_ENABLE)
{
break;
}
title = pc.getSellList().getTitle();
pc.getSellList().updateItems();
for (TradeItem i : pc.getSellList().getItems())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getObjectId());
stm_items.setLong(3, i.getCount());
stm_items.setLong(4, i.getPrice());
stm_items.setLong(5, i.getEnchant());
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
}
case L2PcInstance.STORE_PRIVATE_MANUFACTURE:
{
if (!Config.OFFLINE_CRAFT_ENABLE)
{
break;
}
title = pc.getCreateList().getStoreName();
for (L2ManufactureItem i : pc.getCreateList().getList())
{
stm_items.setInt(1, pc.getObjectId());
stm_items.setInt(2, i.getRecipeId());
stm_items.setLong(3, 0);
stm_items.setLong(4, i.getCost());
stm_items.setLong(5, 0);
stm_items.executeUpdate();
stm_items.clearParameters();
}
break;
}
default:
{
// LOGGER.info( "OfflineTradersTable[storeOffliner()]: Error while saving offline trader: " + pc.getObjectId() + ", store type: "+pc.getPrivateStoreType());
// no save for this kind of shop
save = false;
}
}
if (save)
{
stm.setString(4, title);
stm.executeUpdate();
stm.clearParameters();
con.commit(); // flush
}
}
catch (Exception e)
{
LOGGER.warning("OfflineTradersTable[storeOffliner()]: Error while saving offline trader: " + pc.getObjectId() + " " + e);
}
stm.close();
stm_items.close();
}
catch (Exception e)
{
LOGGER.warning("OfflineTradersTable[storeOffliner()]: Error while saving offline traders: " + e);
}
}
}

View File

@ -0,0 +1,138 @@
/*
* 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.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import com.l2jmobius.gameserver.engines.DocumentEngine;
import com.l2jmobius.gameserver.model.L2Skill;
import com.l2jmobius.gameserver.templates.item.L2WeaponType;
public class SkillTable
{
// private static Logger LOGGER = Logger.getLogger(SkillTable.class);
private static SkillTable _instance;
private final Map<Integer, L2Skill> _skills;
private final boolean _initialized = true;
public static SkillTable getInstance()
{
if (_instance == null)
{
_instance = new SkillTable();
}
return _instance;
}
private SkillTable()
{
_skills = new HashMap<>();
DocumentEngine.getInstance().loadAllSkills(_skills);
}
public void reload()
{
_instance = new SkillTable();
}
public boolean isInitialized()
{
return _initialized;
}
/**
* Provides the skill hash
* @param skill The L2Skill to be hashed
* @return SkillTable.getSkillHashCode(skill.getId(), skill.getLevel())
*/
public static int getSkillHashCode(L2Skill skill)
{
return SkillTable.getSkillHashCode(skill.getId(), skill.getLevel());
}
/**
* Centralized method for easier change of the hashing sys
* @param skillId The Skill Id
* @param skillLevel The Skill Level
* @return The Skill hash number
*/
public static int getSkillHashCode(int skillId, int skillLevel)
{
return (skillId * 256) + skillLevel;
}
public L2Skill getInfo(int skillId, int level)
{
return _skills.get(SkillTable.getSkillHashCode(skillId, level));
}
public int getMaxLevel(int magicId, int level)
{
L2Skill temp;
while (level < 100)
{
level++;
temp = _skills.get(SkillTable.getSkillHashCode(magicId, level));
if (temp == null)
{
return level - 1;
}
}
return level;
}
private static final L2WeaponType[] weaponDbMasks =
{
L2WeaponType.ETC,
L2WeaponType.BOW,
L2WeaponType.POLE,
L2WeaponType.DUALFIST,
L2WeaponType.DUAL,
L2WeaponType.BLUNT,
L2WeaponType.SWORD,
L2WeaponType.DAGGER,
L2WeaponType.BIGSWORD,
L2WeaponType.ROD,
L2WeaponType.BIGBLUNT
};
public int calcWeaponsAllowed(int mask)
{
if (mask == 0)
{
return 0;
}
int weaponsAllowed = 0;
for (int i = 0; i < weaponDbMasks.length; i++)
{
if ((mask & (1 << i)) != 0)
{
weaponsAllowed |= weaponDbMasks[i].mask();
}
}
return weaponsAllowed;
}
}

View File

@ -0,0 +1,414 @@
/*
* 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.gameserver.datatables.csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.geodata.pathfinding.AbstractNodeLoc;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.instancemanager.ClanHallManager;
import com.l2jmobius.gameserver.model.actor.instance.L2DoorInstance;
import com.l2jmobius.gameserver.model.entity.ClanHall;
import com.l2jmobius.gameserver.templates.StatsSet;
import com.l2jmobius.gameserver.templates.chars.L2CharTemplate;
public class DoorTable
{
private static Logger LOGGER = Logger.getLogger(DoorTable.class.getName());
private Map<Integer, L2DoorInstance> _staticItems;
private static DoorTable _instance;
public static DoorTable getInstance()
{
if (_instance == null)
{
_instance = new DoorTable();
}
return _instance;
}
public DoorTable()
{
_staticItems = new HashMap<>();
// parseData();
}
public void reloadAll()
{
respawn();
}
public void respawn()
{
// L2DoorInstance[] currentDoors = getDoors();
_staticItems = null;
_instance = new DoorTable();
}
public void parseData()
{
FileReader reader = null;
BufferedReader buff = null;
LineNumberReader lnr = null;
try
{
final File doorData = new File(Config.DATAPACK_ROOT, "data/csv/door.csv");
reader = new FileReader(doorData);
buff = new BufferedReader(reader);
lnr = new LineNumberReader(buff);
String line = null;
LOGGER.info("Searching clan halls doors:");
while ((line = lnr.readLine()) != null)
{
if ((line.trim().length() == 0) || line.startsWith("#"))
{
continue;
}
final L2DoorInstance door = parseList(line);
_staticItems.put(door.getDoorId(), door);
door.spawnMe(door.getX(), door.getY(), door.getZ());
final ClanHall clanhall = ClanHallManager.getInstance().getNearbyClanHall(door.getX(), door.getY(), 500);
if (clanhall != null)
{
clanhall.getDoors().add(door);
door.setClanHall(clanhall);
if (Config.DEBUG)
{
LOGGER.warning("door " + door.getDoorName() + " attached to ch " + clanhall.getName());
}
}
}
LOGGER.info("DoorTable: Loaded " + _staticItems.size() + " Door Templates.");
}
catch (FileNotFoundException e)
{
_initialized = false;
LOGGER.warning("door.csv is missing in data csv folder");
}
catch (IOException e)
{
_initialized = false;
LOGGER.warning("error while creating door table " + e);
}
finally
{
if (lnr != null)
{
try
{
lnr.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (buff != null)
{
try
{
buff.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (reader != null)
{
try
{
reader.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
public static L2DoorInstance parseList(String line)
{
StringTokenizer st = new StringTokenizer(line, ";");
String name = st.nextToken();
final int id = Integer.parseInt(st.nextToken());
final int x = Integer.parseInt(st.nextToken());
final int y = Integer.parseInt(st.nextToken());
final int z = Integer.parseInt(st.nextToken());
final int rangeXMin = Integer.parseInt(st.nextToken());
final int rangeYMin = Integer.parseInt(st.nextToken());
final int rangeZMin = Integer.parseInt(st.nextToken());
final int rangeXMax = Integer.parseInt(st.nextToken());
final int rangeYMax = Integer.parseInt(st.nextToken());
final int rangeZMax = Integer.parseInt(st.nextToken());
final int hp = Integer.parseInt(st.nextToken());
final int pdef = Integer.parseInt(st.nextToken());
final int mdef = Integer.parseInt(st.nextToken());
boolean unlockable = false;
if (st.hasMoreTokens())
{
unlockable = Boolean.parseBoolean(st.nextToken());
}
boolean autoOpen = false;
if (st.hasMoreTokens())
{
autoOpen = Boolean.parseBoolean(st.nextToken());
}
if (rangeXMin > rangeXMax)
{
LOGGER.warning("Error in door data, ID:" + id);
}
if (rangeYMin > rangeYMax)
{
LOGGER.warning("Error in door data, ID:" + id);
}
if (rangeZMin > rangeZMax)
{
LOGGER.warning("Error in door data, ID:" + id);
}
int collisionRadius; // (max) radius for movement checks
if ((rangeXMax - rangeXMin) > (rangeYMax - rangeYMin))
{
collisionRadius = rangeYMax - rangeYMin;
}
else
{
collisionRadius = rangeXMax - rangeXMin;
}
StatsSet npcDat = new StatsSet();
npcDat.set("npcId", id);
npcDat.set("level", 0);
npcDat.set("jClass", "door");
npcDat.set("baseSTR", 0);
npcDat.set("baseCON", 0);
npcDat.set("baseDEX", 0);
npcDat.set("baseINT", 0);
npcDat.set("baseWIT", 0);
npcDat.set("baseMEN", 0);
npcDat.set("baseShldDef", 0);
npcDat.set("baseShldRate", 0);
npcDat.set("baseAccCombat", 38);
npcDat.set("baseEvasRate", 38);
npcDat.set("baseCritRate", 38);
// npcDat.set("name", "");
npcDat.set("collision_radius", collisionRadius);
npcDat.set("collision_height", rangeZMax - rangeZMin);
npcDat.set("sex", "male");
npcDat.set("type", "");
npcDat.set("baseAtkRange", 0);
npcDat.set("baseMpMax", 0);
npcDat.set("baseCpMax", 0);
npcDat.set("rewardExp", 0);
npcDat.set("rewardSp", 0);
npcDat.set("basePAtk", 0);
npcDat.set("baseMAtk", 0);
npcDat.set("basePAtkSpd", 0);
npcDat.set("aggroRange", 0);
npcDat.set("baseMAtkSpd", 0);
npcDat.set("rhand", 0);
npcDat.set("lhand", 0);
npcDat.set("armor", 0);
npcDat.set("baseWalkSpd", 0);
npcDat.set("baseRunSpd", 0);
npcDat.set("name", name);
npcDat.set("baseHpMax", hp);
npcDat.set("baseHpReg", 3.e-3f);
npcDat.set("baseMpReg", 3.e-3f);
npcDat.set("basePDef", pdef);
npcDat.set("baseMDef", mdef);
L2CharTemplate template = new L2CharTemplate(npcDat);
final L2DoorInstance door = new L2DoorInstance(IdFactory.getInstance().getNextId(), template, id, name, unlockable);
door.setRange(rangeXMin, rangeYMin, rangeZMin, rangeXMax, rangeYMax, rangeZMax);
try
{
door.setMapRegion(MapRegionTable.getInstance().getMapRegion(x, y));
}
catch (Exception e)
{
LOGGER.warning("Error in door data, ID:" + id + " " + e);
}
door.setCurrentHpMp(door.getMaxHp(), door.getMaxMp());
door.setOpen(autoOpen);
door.setXYZInvisible(x, y, z);
return door;
}
public boolean isInitialized()
{
return _initialized;
}
private boolean _initialized = true;
public L2DoorInstance getDoor(Integer id)
{
return _staticItems.get(id);
}
public void putDoor(L2DoorInstance door)
{
_staticItems.put(door.getDoorId(), door);
}
public L2DoorInstance[] getDoors()
{
final L2DoorInstance[] _allTemplates = _staticItems.values().toArray(new L2DoorInstance[_staticItems.size()]);
return _allTemplates;
}
/**
* Performs a check and sets up a scheduled task for those doors that require auto opening/closing.
*/
public void checkAutoOpen()
{
for (L2DoorInstance doorInst : getDoors())
{
// Garden of Eva (every 7 minutes)
if (doorInst.getDoorName().startsWith("goe"))
{
doorInst.setAutoActionDelay(420000);
}
// Tower of Insolence (every 5 minutes)
else if (doorInst.getDoorName().startsWith("aden_tower"))
{
doorInst.setAutoActionDelay(300000);
}
// Cruma Tower (every 20 minutes)
else if (doorInst.getDoorName().startsWith("cruma"))
{
doorInst.setAutoActionDelay(1200000);
}
}
}
public boolean checkIfDoorsBetween(AbstractNodeLoc start, AbstractNodeLoc end)
{
return checkIfDoorsBetween(start.getX(), start.getY(), start.getZ(), end.getX(), end.getY(), end.getZ());
}
public boolean checkIfDoorsBetween(int x, int y, int z, int tx, int ty, int tz)
{
int region;
try
{
region = MapRegionTable.getInstance().getMapRegion(x, y);
}
catch (Exception e)
{
return false;
}
for (L2DoorInstance doorInst : getDoors())
{
if (doorInst.getMapRegion() != region)
{
continue;
}
if (doorInst.getXMax() == 0)
{
continue;
}
// line segment goes through box
// heavy approximation disabling some shooting angles especially near 2-piece doors
// but most calculations should stop short
// phase 1, x
if (((x <= doorInst.getXMax()) && (tx >= doorInst.getXMin())) || ((tx <= doorInst.getXMax()) && (x >= doorInst.getXMin())))
{
// phase 2, y
if (((y <= doorInst.getYMax()) && (ty >= doorInst.getYMin())) || ((ty <= doorInst.getYMax()) && (y >= doorInst.getYMin())))
{
// phase 3, basically only z remains but now we calculate it with another formula (by rage)
// in some cases the direct line check (only) in the beginning isn't sufficient,
// when char z changes a lot along the path
if ((doorInst.getStatus().getCurrentHp() > 0) && !doorInst.getOpen())
{
final int px1 = doorInst.getXMin();
final int py1 = doorInst.getYMin();
final int pz1 = doorInst.getZMin();
final int px2 = doorInst.getXMax();
final int py2 = doorInst.getYMax();
final int pz2 = doorInst.getZMax();
final int l = tx - x;
final int m = ty - y;
final int n = tz - z;
int dk = ((doorInst.getA() * l) + (doorInst.getB() * m) + (doorInst.getC() * n));
if (dk == 0)
{
continue; // Parallel
}
final float p = (float) ((doorInst.getA() * x) + (doorInst.getB() * y) + (doorInst.getC() * z) + doorInst.getD()) / (float) dk;
final int fx = (int) (x - (l * p));
final int fy = (int) (y - (m * p));
final int fz = (int) (z - (n * p));
if (((Math.min(x, tx) <= fx) && (fx <= Math.max(x, tx))) && ((Math.min(y, ty) <= fy) && (fy <= Math.max(y, ty))) && ((Math.min(z, tz) <= fz) && (fz <= Math.max(z, tz))))
{
if ((((fx >= px1) && (fx <= px2)) || ((fx >= px2) && (fx <= px1))) && (((fy >= py1) && (fy <= py2)) || ((fy >= py2) && (fy <= py1))) && (((fz >= pz1) && (fz <= pz2)) || ((fz >= pz2) && (fz <= pz1))))
{
return true; // Door between
}
}
}
}
}
}
return false;
}
}

View File

@ -0,0 +1,178 @@
/*
* 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.gameserver.datatables.csv;
import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.model.L2ExtractableItem;
import com.l2jmobius.gameserver.model.L2ExtractableProductItem;
/**
* @author FBIagent
*/
public class ExtractableItemsData
{
private static Logger LOGGER = Logger.getLogger(ExtractableItemsData.class.getName());
// Map<itemid, L2ExtractableItem>
private Map<Integer, L2ExtractableItem> _items;
private static ExtractableItemsData _instance = null;
public static ExtractableItemsData getInstance()
{
if (_instance == null)
{
_instance = new ExtractableItemsData();
}
return _instance;
}
public ExtractableItemsData()
{
_items = new HashMap<>();
Scanner s = null;
try
{
s = new Scanner(new File(Config.DATAPACK_ROOT + "/data/csv/extractable_items.csv"));
int lineCount = 0;
while (s.hasNextLine())
{
lineCount++;
final String line = s.nextLine();
if (line.startsWith("#"))
{
continue;
}
else if (line.equals(""))
{
continue;
}
final String[] lineSplit = line.split(";");
int itemID = 0;
try
{
itemID = Integer.parseInt(lineSplit[0]);
}
catch (Exception e)
{
LOGGER.info("Extractable items data: Error in line " + lineCount + " -> invalid item id or wrong seperator after item id!");
LOGGER.info(" " + line);
return;
}
final List<L2ExtractableProductItem> product_temp = new ArrayList<>(lineSplit.length);
for (int i = 0; i < (lineSplit.length - 1); i++)
{
String[] lineSplit2 = lineSplit[i + 1].split(",");
if (lineSplit2.length != 3)
{
LOGGER.info("Extractable items data: Error in line " + lineCount + " -> wrong seperator!");
LOGGER.info(" " + line);
continue;
}
int production = 0, amount = 0, chance = 0;
try
{
production = Integer.parseInt(lineSplit2[0]);
amount = Integer.parseInt(lineSplit2[1]);
chance = Integer.parseInt(lineSplit2[2]);
}
catch (Exception e)
{
LOGGER.info("Extractable items data: Error in line " + lineCount + " -> incomplete/invalid production data or wrong seperator!");
LOGGER.info(" " + line);
continue;
}
product_temp.add(new L2ExtractableProductItem(production, amount, chance));
}
int fullChances = 0;
for (L2ExtractableProductItem Pi : product_temp)
{
fullChances += Pi.getChance();
}
if (fullChances > 100)
{
LOGGER.info("Extractable items data: Error in line " + lineCount + " -> all chances together are more then 100!");
LOGGER.info(" " + line);
continue;
}
_items.put(itemID, new L2ExtractableItem(itemID, product_temp));
}
LOGGER.info("Extractable items data: Loaded " + _items.size() + " extractable items!");
}
catch (Exception e)
{
// if(Config.ENABLE_ALL_EXCEPTIONS)
e.printStackTrace();
LOGGER.info("Extractable items data: Can not find './data/csv/extractable_items.csv'");
}
finally
{
if (s != null)
{
try
{
s.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
public L2ExtractableItem getExtractableItem(int itemID)
{
return _items.get(itemID);
}
public int[] itemIDs()
{
final int size = _items.size();
final int[] result = new int[size];
int i = 0;
for (L2ExtractableItem ei : _items.values())
{
result[i] = ei.getItemId();
i++;
}
return result;
}
}

View File

@ -0,0 +1,225 @@
/*
* 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.gameserver.datatables.csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.datatables.sql.SkillTreeTable;
import com.l2jmobius.gameserver.model.FishData;
/**
* @author -Nemesiss-
*/
public class FishTable
{
private static Logger LOGGER = Logger.getLogger(SkillTreeTable.class.getName());
private static final FishTable _instance = new FishTable();
private static List<FishData> _fishsNormal;
private static List<FishData> _fishsEasy;
private static List<FishData> _fishsHard;
public static FishData fish;
public static FishTable getInstance()
{
return _instance;
}
private FishTable()
{
int count = 0;
FileReader reader = null;
BufferedReader buff = null;
LineNumberReader lnr = null;
try
{
final File fileData = new File(Config.DATAPACK_ROOT + "/data/csv/fish.csv");
reader = new FileReader(fileData);
buff = new BufferedReader(reader);
lnr = new LineNumberReader(buff);
String line = null;
_fishsEasy = new ArrayList<>();
_fishsNormal = new ArrayList<>();
_fishsHard = new ArrayList<>();
FishData fish;
// format:
// id;level;name;hp;hpregen;fish_type;fish_group;fish_guts;guts_check_time;wait_time;combat_time
while ((line = lnr.readLine()) != null)
{
// ignore comments
if ((line.trim().length() == 0) || line.startsWith("#"))
{
continue;
}
final StringTokenizer st = new StringTokenizer(line, ";");
final int id = Integer.parseInt(st.nextToken());
final int lvl = Integer.parseInt(st.nextToken());
final String name = st.nextToken();
final int hp = Integer.parseInt(st.nextToken());
final int hpreg = Integer.parseInt(st.nextToken());
final int type = Integer.parseInt(st.nextToken());
final int group = Integer.parseInt(st.nextToken());
final int fish_guts = Integer.parseInt(st.nextToken());
final int guts_check_time = Integer.parseInt(st.nextToken());
final int wait_time = Integer.parseInt(st.nextToken());
final int combat_time = Integer.parseInt(st.nextToken());
fish = new FishData(id, lvl, name, hp, hpreg, type, group, fish_guts, guts_check_time, wait_time, combat_time);
switch (fish.getGroup())
{
case 0:
{
_fishsEasy.add(fish);
break;
}
case 1:
{
_fishsNormal.add(fish);
break;
}
case 2:
{
_fishsHard.add(fish);
}
}
}
count = _fishsEasy.size() + _fishsNormal.size() + _fishsHard.size();
}
catch (FileNotFoundException e)
{
LOGGER.warning("fish.csv is missing in data folder");
}
catch (IOException e0)
{
LOGGER.warning("Error while creating table: " + e0.getMessage() + "\n" + e0);
}
finally
{
if (lnr != null)
{
try
{
lnr.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (buff != null)
{
try
{
buff.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (reader != null)
{
try
{
reader.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
LOGGER.info("FishTable: Loaded " + count + " Fishes.");
}
/**
* @param lvl
* @param type
* @param group
* @return List of Fish that can be fished
*/
public List<FishData> getfish(int lvl, int type, int group)
{
final List<FishData> result = new ArrayList<>();
List<FishData> _Fishs = null;
switch (group)
{
case 0:
{
_Fishs = _fishsEasy;
break;
}
case 1:
{
_Fishs = _fishsNormal;
break;
}
case 2:
{
_Fishs = _fishsHard;
}
}
if (_Fishs == null)
{
// the fish list is empty
LOGGER.warning("Fish are not defined !");
return null;
}
for (FishData f : _Fishs)
{
if (f.getLevel() != lvl)
{
continue;
}
if (f.getType() != type)
{
continue;
}
result.add(f);
}
if (result.size() == 0)
{
LOGGER.warning("Cant Find Any Fish!? - Lvl: " + lvl + " Type: " + type);
}
return result;
}
}

View File

@ -0,0 +1,167 @@
/*
* 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.gameserver.datatables.csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.templates.StatsSet;
import com.l2jmobius.gameserver.templates.item.L2Henna;
/**
* This class ...
* @version $Revision$ $Date$
*/
public class HennaTable
{
private static Logger LOGGER = Logger.getLogger(HennaTable.class.getName());
private static HennaTable _instance;
private final Map<Integer, L2Henna> _henna;
private final boolean _initialized = true;
public static HennaTable getInstance()
{
if (_instance == null)
{
_instance = new HennaTable();
}
return _instance;
}
private HennaTable()
{
_henna = new HashMap<>();
restoreHennaData();
}
private void restoreHennaData()
{
FileReader reader = null;
BufferedReader buff = null;
LineNumberReader lnr = null;
try
{
final File fileData = new File(Config.DATAPACK_ROOT + "/data/csv/henna.csv");
reader = new FileReader(fileData);
buff = new BufferedReader(reader);
lnr = new LineNumberReader(buff);
String line = null;
while ((line = lnr.readLine()) != null)
{
// ignore comments
if ((line.trim().length() == 0) || line.startsWith("#"))
{
continue;
}
final StringTokenizer st = new StringTokenizer(line, ";");
StatsSet hennaDat = new StatsSet();
final int id = Integer.parseInt(st.nextToken());
hennaDat.set("symbol_id", id);
st.nextToken(); // next token...ignore name
hennaDat.set("dye", Integer.parseInt(st.nextToken()));
hennaDat.set("amount", Integer.parseInt(st.nextToken()));
hennaDat.set("price", Integer.parseInt(st.nextToken()));
hennaDat.set("stat_INT", Integer.parseInt(st.nextToken()));
hennaDat.set("stat_STR", Integer.parseInt(st.nextToken()));
hennaDat.set("stat_CON", Integer.parseInt(st.nextToken()));
hennaDat.set("stat_MEM", Integer.parseInt(st.nextToken()));
hennaDat.set("stat_DEX", Integer.parseInt(st.nextToken()));
hennaDat.set("stat_WIT", Integer.parseInt(st.nextToken()));
L2Henna template = new L2Henna(hennaDat);
_henna.put(id, template);
}
LOGGER.info("HennaTable: Loaded " + _henna.size() + " Templates.");
}
catch (FileNotFoundException e)
{
LOGGER.warning(Config.DATAPACK_ROOT + "/data/csv/henna.csv is missing in data folder");
}
catch (IOException e0)
{
LOGGER.warning("Error while creating table: " + e0.getMessage() + "\n" + e0);
}
finally
{
if (lnr != null)
{
try
{
lnr.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (buff != null)
{
try
{
buff.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (reader != null)
{
try
{
reader.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
public boolean isInitialized()
{
return _initialized;
}
public L2Henna getTemplate(int id)
{
return _henna.get(id);
}
}

View File

@ -0,0 +1,623 @@
/*
* 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.gameserver.datatables.csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.instancemanager.ArenaManager;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.ClanHallManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
import com.l2jmobius.gameserver.instancemanager.TownManager;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.instance.L2NpcInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.actor.position.Location;
import com.l2jmobius.gameserver.model.entity.ClanHall;
import com.l2jmobius.gameserver.model.entity.siege.Castle;
import com.l2jmobius.gameserver.model.entity.siege.Fort;
import com.l2jmobius.gameserver.model.zone.ZoneId;
import com.l2jmobius.gameserver.model.zone.type.L2ArenaZone;
import com.l2jmobius.gameserver.model.zone.type.L2ClanHallZone;
import com.l2jmobius.gameserver.model.zone.type.L2TownZone;
public class MapRegionTable
{
private static Logger LOGGER = Logger.getLogger(MapRegionTable.class.getName());
private static MapRegionTable _instance;
private final int[][] _regions = new int[19][21];
private final int[][] _pointsWithKarmas;
public enum TeleportWhereType
{
Castle,
ClanHall,
SiegeFlag,
Town,
Fortress
}
public static MapRegionTable getInstance()
{
if (_instance == null)
{
_instance = new MapRegionTable();
}
return _instance;
}
private MapRegionTable()
{
FileReader reader = null;
BufferedReader buff = null;
LineNumberReader lnr = null;
try
{
final File fileData = new File(Config.DATAPACK_ROOT + "/data/csv/mapregion.csv");
reader = new FileReader(fileData);
buff = new BufferedReader(reader);
lnr = new LineNumberReader(buff);
String line = null;
int region;
while ((line = lnr.readLine()) != null)
{
// ignore comments
if ((line.trim().length() == 0) || line.startsWith("#"))
{
continue;
}
final StringTokenizer st = new StringTokenizer(line, ";");
region = Integer.parseInt(st.nextToken());
for (int j = 0; j < 10; j++)
{
_regions[j][region] = Integer.parseInt(st.nextToken());
}
}
}
catch (FileNotFoundException e)
{
LOGGER.warning("mapregion.csv is missing in data folder");
}
catch (NoSuchElementException e1)
{
LOGGER.warning("Error for structure CSV file: ");
e1.printStackTrace();
}
catch (IOException e0)
{
LOGGER.warning("Error while creating table: " + e0);
e0.printStackTrace();
}
finally
{
if (lnr != null)
{
try
{
lnr.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (buff != null)
{
try
{
buff.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (reader != null)
{
try
{
reader.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
_pointsWithKarmas = new int[19][3];
// Talking Island
_pointsWithKarmas[0][0] = -79077;
_pointsWithKarmas[0][1] = 240355;
_pointsWithKarmas[0][2] = -3440;
// Elven
_pointsWithKarmas[1][0] = 43503;
_pointsWithKarmas[1][1] = 40398;
_pointsWithKarmas[1][2] = -3450;
// DarkElven
_pointsWithKarmas[2][0] = 1675;
_pointsWithKarmas[2][1] = 19581;
_pointsWithKarmas[2][2] = -3110;
// Orc
_pointsWithKarmas[3][0] = -44413;
_pointsWithKarmas[3][1] = -121762;
_pointsWithKarmas[3][2] = -235;
// Dwalf
_pointsWithKarmas[4][0] = 12009;
_pointsWithKarmas[4][1] = -187319;
_pointsWithKarmas[4][2] = -3309;
// Gludio
_pointsWithKarmas[5][0] = -18872;
_pointsWithKarmas[5][1] = 126216;
_pointsWithKarmas[5][2] = -3280;
// Gludin
_pointsWithKarmas[6][0] = -85915;
_pointsWithKarmas[6][1] = 150402;
_pointsWithKarmas[6][2] = -3060;
// Dion
_pointsWithKarmas[7][0] = 23652;
_pointsWithKarmas[7][1] = 144823;
_pointsWithKarmas[7][2] = -3330;
// Giran
_pointsWithKarmas[8][0] = 79125;
_pointsWithKarmas[8][1] = 154197;
_pointsWithKarmas[8][2] = -3490;
// Oren
_pointsWithKarmas[9][0] = 73840;
_pointsWithKarmas[9][1] = 58193;
_pointsWithKarmas[9][2] = -2730;
// Aden
_pointsWithKarmas[10][0] = 44413;
_pointsWithKarmas[10][1] = 22610;
_pointsWithKarmas[10][2] = 235;
// Hunters
_pointsWithKarmas[11][0] = 114137;
_pointsWithKarmas[11][1] = 72993;
_pointsWithKarmas[11][2] = -2445;
// Giran
_pointsWithKarmas[12][0] = 79125;
_pointsWithKarmas[12][1] = 154197;
_pointsWithKarmas[12][2] = -3490;
// heine
_pointsWithKarmas[13][0] = 119536;
_pointsWithKarmas[13][1] = 218558;
_pointsWithKarmas[13][2] = -3495;
// Rune Castle Town
_pointsWithKarmas[14][0] = 42931;
_pointsWithKarmas[14][1] = -44733;
_pointsWithKarmas[14][2] = -1326;
// Goddard
_pointsWithKarmas[15][0] = 147419;
_pointsWithKarmas[15][1] = -64980;
_pointsWithKarmas[15][2] = -3457;
// Schuttgart
_pointsWithKarmas[16][0] = 85184;
_pointsWithKarmas[16][1] = -138560;
_pointsWithKarmas[16][2] = -2256;
// TODO Primeval Isle
_pointsWithKarmas[18][0] = 10468;
_pointsWithKarmas[18][1] = -24569;
_pointsWithKarmas[18][2] = -3645;
}
public final int getMapRegion(int posX, int posY)
{
return _regions[getMapRegionX(posX)][getMapRegionY(posY)];
}
public final int getMapRegionX(int posX)
{
return (posX >> 15) + 4;// + centerTileX;
}
public final int getMapRegionY(int posY)
{
return (posY >> 15) + 10;// + centerTileX;
}
public int getAreaCastle(L2Character activeChar)
{
final int area = getClosestTownNumber(activeChar);
int castle;
switch (area)
{
case 0:
{
castle = 1;
break;// Talking Island Village
}
case 1:
{
castle = 4;
break; // Elven Village
}
case 2:
{
castle = 4;
break; // Dark Elven Village
}
case 3:
{
castle = 9;
break; // Orc Village
}
case 4:
{
castle = 9;
break; // Dwarven Village
}
case 5:
{
castle = 1;
break; // Town of Gludio
}
case 6:
{
castle = 1;
break; // Gludin Village
}
case 7:
{
castle = 2;
break; // Town of Dion
}
case 8:
{
castle = 3;
break; // Town of Giran
}
case 9:
{
castle = 4;
break; // Town of Oren
}
case 10:
{
castle = 5;
break; // Town of Aden
}
case 11:
{
castle = 5;
break; // Hunters Village
}
case 12:
{
castle = 3;
break; // Giran Harbor
}
case 13:
{
castle = 6;
break; // Heine
}
case 14:
{
castle = 8;
break; // Rune Township
}
case 15:
{
castle = 7;
break; // Town of Goddard
}
case 16:
{
castle = 9;
break; // Town of Shuttgart
}
case 17:
{
castle = 4;
break; // Ivory Tower
}
case 18:
{
castle = 8;
break; // Primeval Isle Wharf
}
default:
{
castle = 5;
break; // Town of Aden
}
}
return castle;
}
public int getClosestTownNumber(L2Character activeChar)
{
return getMapRegion(activeChar.getX(), activeChar.getY());
}
public String getClosestTownName(L2Character activeChar)
{
final int nearestTownId = getMapRegion(activeChar.getX(), activeChar.getY());
String nearestTown;
switch (nearestTownId)
{
case 0:
{
nearestTown = "Talking Island Village";
break;
}
case 1:
{
nearestTown = "Elven Village";
break;
}
case 2:
{
nearestTown = "Dark Elven Village";
break;
}
case 3:
{
nearestTown = "Orc Village";
break;
}
case 4:
{
nearestTown = "Dwarven Village";
break;
}
case 5:
{
nearestTown = "Town of Gludio";
break;
}
case 6:
{
nearestTown = "Gludin Village";
break;
}
case 7:
{
nearestTown = "Town of Dion";
break;
}
case 8:
{
nearestTown = "Town of Giran";
break;
}
case 9:
{
nearestTown = "Town of Oren";
break;
}
case 10:
{
nearestTown = "Town of Aden";
break;
}
case 11:
{
nearestTown = "Hunters Village";
break;
}
case 12:
{
nearestTown = "Giran Harbor";
break;
}
case 13:
{
nearestTown = "Heine";
break;
}
case 14:
{
nearestTown = "Rune Township";
break;
}
case 15:
{
nearestTown = "Town of Goddard";
break;
}
case 16:
{
nearestTown = "Town of Shuttgart";
break; // //TODO@ (Check mapregion table)[Luno]
}
case 18:
{
nearestTown = "Primeval Isle";
break;
}
default:
{
nearestTown = "Town of Aden";
break;
}
}
return nearestTown;
}
public Location getTeleToLocation(L2Character activeChar, TeleportWhereType teleportWhere)
{
int[] coord;
if (activeChar instanceof L2PcInstance)
{
final L2PcInstance player = (L2PcInstance) activeChar;
// If in Monster Derby Track
if (player.isInsideZone(ZoneId.MONSTERTRACK))
{
return new Location(12661, 181687, -3560);
}
Castle castle = null;
Fort fort = null;
ClanHall clanhall = null;
if (player.getClan() != null)
{
// If teleport to clan hall
if (teleportWhere == TeleportWhereType.ClanHall)
{
clanhall = ClanHallManager.getInstance().getClanHallByOwner(player.getClan());
if (clanhall != null)
{
final L2ClanHallZone zone = clanhall.getZone();
if (zone != null)
{
return zone.getSpawn();
}
}
}
// If teleport to castle
if (teleportWhere == TeleportWhereType.Castle)
{
castle = CastleManager.getInstance().getCastleByOwner(player.getClan());
}
// If teleport to fort
if (teleportWhere == TeleportWhereType.Fortress)
{
fort = FortManager.getInstance().getFortByOwner(player.getClan());
}
// Check if player is on castle&fortress ground
if (castle == null)
{
castle = CastleManager.getInstance().getCastle(player);
}
if (fort == null)
{
fort = FortManager.getInstance().getFort(player);
}
if ((castle != null) && (castle.getCastleId() > 0))
{
// If Teleporting to castle or
// If is on caslte with siege and player's clan is defender
if ((teleportWhere == TeleportWhereType.Castle) || ((teleportWhere == TeleportWhereType.Castle) && castle.getSiege().getIsInProgress() && (castle.getSiege().getDefenderClan(player.getClan()) != null)))
{
coord = castle.getZone().getSpawn();
return new Location(coord[0], coord[1], coord[2]);
}
if ((teleportWhere == TeleportWhereType.SiegeFlag) && castle.getSiege().getIsInProgress())
{
// Check if player's clan is attacker
List<L2NpcInstance> flags = castle.getSiege().getFlag(player.getClan());
if ((flags != null) && !flags.isEmpty())
{
// Spawn to flag - Need more work to get player to the nearest flag
final L2NpcInstance flag = flags.get(0);
return new Location(flag.getX(), flag.getY(), flag.getZ());
}
}
}
else if ((fort != null) && (fort.getFortId() > 0))
{
// teleporting to castle or fortress
// is on caslte with siege and player's clan is defender
if ((teleportWhere == TeleportWhereType.Fortress) || ((teleportWhere == TeleportWhereType.Fortress) && fort.getSiege().getIsInProgress() && (fort.getSiege().getDefenderClan(player.getClan()) != null)))
{
coord = fort.getZone().getSpawn();
return new Location(coord[0], coord[1], coord[2]);
}
if ((teleportWhere == TeleportWhereType.SiegeFlag) && fort.getSiege().getIsInProgress())
{
// check if player's clan is attacker
List<L2NpcInstance> flags = fort.getSiege().getFlag(player.getClan());
if ((flags != null) && !flags.isEmpty())
{
// spawn to flag
final L2NpcInstance flag = flags.get(0);
return new Location(flag.getX(), flag.getY(), flag.getZ());
}
}
}
}
// teleport RED PK 5+ to Floran Village
if ((player.getPkKills() > 5) && (player.getKarma() > 1))
{
return new Location(17817, 170079, -3530);
}
// Karma player land out of city
if (player.getKarma() > 1)
{
final int closest = getMapRegion(activeChar.getX(), activeChar.getY());
if ((closest >= 0) && (closest < _pointsWithKarmas.length))
{
return new Location(_pointsWithKarmas[closest][0], _pointsWithKarmas[closest][1], _pointsWithKarmas[closest][2]);
}
return new Location(17817, 170079, -3530);
}
// Checking if in arena
final L2ArenaZone arena = ArenaManager.getInstance().getArena(player);
if (arena != null)
{
coord = arena.getSpawnLoc();
return new Location(coord[0], coord[1], coord[2]);
}
}
// Get the nearest town
L2TownZone local_zone = null;
if ((activeChar != null) && ((local_zone = TownManager.getInstance().getClosestTown(activeChar)) != null))
{
coord = local_zone.getSpawnLoc();
return new Location(coord[0], coord[1], coord[2]);
}
local_zone = TownManager.getInstance().getTown(9); // giran
coord = local_zone.getSpawnLoc();
return new Location(coord[0], coord[1], coord[2]);
}
}

View File

@ -0,0 +1,180 @@
/*
* 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.gameserver.datatables.csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.model.L2NpcWalkerNode;
/**
* Main Table to Load Npc Walkers Routes and Chat SQL Table.<br>
* @author Rayan RPG for L2Emu Project
* @since 927
*/
public class NpcWalkerRoutesTable
{
protected static final Logger LOGGER = Logger.getLogger(NpcWalkerRoutesTable.class.getName());
private static NpcWalkerRoutesTable _instance;
private List<L2NpcWalkerNode> _routes;
public static NpcWalkerRoutesTable getInstance()
{
if (_instance == null)
{
_instance = new NpcWalkerRoutesTable();
LOGGER.info("Initializing Walkers Routes Table.");
}
return _instance;
}
private NpcWalkerRoutesTable()
{
// not here
}
public void load()
{
_routes = new ArrayList<>();
FileReader reader = null;
BufferedReader buff = null;
LineNumberReader lnr = null;
try
{
final File fileData = new File(Config.DATAPACK_ROOT + "/data/csv/walker_routes.csv");
reader = new FileReader(fileData);
buff = new BufferedReader(reader);
lnr = new LineNumberReader(buff);
L2NpcWalkerNode route;
String line = null;
// format:
// route_id;npc_id;move_point;chatText;move_x;move_y;move_z;delay;running
while ((line = lnr.readLine()) != null)
{
// ignore comments
if ((line.trim().length() == 0) || line.startsWith("#"))
{
continue;
}
route = new L2NpcWalkerNode();
final StringTokenizer st = new StringTokenizer(line, ";");
final int route_id = Integer.parseInt(st.nextToken());
final int npc_id = Integer.parseInt(st.nextToken());
final String move_point = st.nextToken();
final String chatText = st.nextToken();
final int move_x = Integer.parseInt(st.nextToken());
final int move_y = Integer.parseInt(st.nextToken());
final int move_z = Integer.parseInt(st.nextToken());
final int delay = Integer.parseInt(st.nextToken());
final boolean running = Boolean.parseBoolean(st.nextToken());
route.setRouteId(route_id);
route.setNpcId(npc_id);
route.setMovePoint(move_point);
route.setChatText(chatText);
route.setMoveX(move_x);
route.setMoveY(move_y);
route.setMoveZ(move_z);
route.setDelay(delay);
route.setRunning(running);
_routes.add(route);
}
LOGGER.info("WalkerRoutesTable: Loaded " + _routes.size() + " Npc Walker Routes.");
}
catch (FileNotFoundException e)
{
LOGGER.warning("walker_routes.csv is missing in data folder");
}
catch (IOException e0)
{
LOGGER.warning("Error while creating table: " + e0.getMessage() + "\n" + e0);
}
finally
{
if (lnr != null)
{
try
{
lnr.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (buff != null)
{
try
{
buff.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (reader != null)
{
try
{
reader.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
public List<L2NpcWalkerNode> getRouteForNpc(int id)
{
final List<L2NpcWalkerNode> _return = new ArrayList<>();
for (L2NpcWalkerNode node : _routes)
{
if (node.getNpcId() == id)
{
_return.add(node);
}
}
return _return;
}
}

View File

@ -0,0 +1,236 @@
/*
* 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.gameserver.datatables.csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.LineNumberReader;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.RecipeController;
import com.l2jmobius.gameserver.model.L2RecipeList;
import com.l2jmobius.gameserver.model.actor.instance.L2RecipeInstance;
/**
* @author programmos
*/
public class RecipeTable extends RecipeController
{
private static final Logger LOGGER = Logger.getLogger(RecipeTable.class.getName());
private final Map<Integer, L2RecipeList> _lists;
private static RecipeTable instance;
public static RecipeTable getInstance()
{
if (instance == null)
{
instance = new RecipeTable();
}
return instance;
}
private RecipeTable()
{
_lists = new HashMap<>();
String line = null;
FileReader reader = null;
BufferedReader buff = null;
LineNumberReader lnr = null;
try
{
final File recipesData = new File(Config.DATAPACK_ROOT, "data/recipes.csv");
reader = new FileReader(recipesData);
buff = new BufferedReader(reader);
lnr = new LineNumberReader(buff);
while ((line = lnr.readLine()) != null)
{
if ((line.trim().length() == 0) || line.startsWith("#"))
{
continue;
}
parseList(line);
}
LOGGER.info("RecipeController: Loaded " + _lists.size() + " Recipes.");
}
catch (Exception e)
{
if (lnr != null)
{
LOGGER.warning("error while creating recipe controller in linenr: " + lnr.getLineNumber() + " " + e);
}
else
{
LOGGER.warning("No recipes were found in data folder");
}
}
finally
{
if (lnr != null)
{
try
{
lnr.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (buff != null)
{
try
{
buff.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (reader != null)
{
try
{
reader.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
// TODO XMLize the recipe list
private void parseList(String line)
{
try
{
StringTokenizer st = new StringTokenizer(line, ";");
final List<L2RecipeInstance> recipePartList = new ArrayList<>();
// we use common/dwarf for easy reading of the recipes.csv file
String recipeTypeString = st.nextToken();
// now parse the string into a boolean
boolean isDwarvenRecipe;
if (recipeTypeString.equalsIgnoreCase("dwarven"))
{
isDwarvenRecipe = true;
}
else if (recipeTypeString.equalsIgnoreCase("common"))
{
isDwarvenRecipe = false;
}
else
{ // prints a helpfull message
LOGGER.warning("Error parsing recipes.csv, unknown recipe type " + recipeTypeString);
return;
}
String recipeName = st.nextToken();
final int id = Integer.parseInt(st.nextToken());
final int recipeId = Integer.parseInt(st.nextToken());
final int level = Integer.parseInt(st.nextToken());
// material
StringTokenizer st2 = new StringTokenizer(st.nextToken(), "[],");
while (st2.hasMoreTokens())
{
StringTokenizer st3 = new StringTokenizer(st2.nextToken(), "()");
final int rpItemId = Integer.parseInt(st3.nextToken());
final int quantity = Integer.parseInt(st3.nextToken());
L2RecipeInstance rp = new L2RecipeInstance(rpItemId, quantity);
recipePartList.add(rp);
}
final int itemId = Integer.parseInt(st.nextToken());
final int count = Integer.parseInt(st.nextToken());
// npc fee
/* String notdoneyet = */st.nextToken();
final int mpCost = Integer.parseInt(st.nextToken());
final int successRate = Integer.parseInt(st.nextToken());
L2RecipeList recipeList = new L2RecipeList(id, level, recipeId, recipeName, successRate, mpCost, itemId, count, isDwarvenRecipe);
for (L2RecipeInstance recipePart : recipePartList)
{
recipeList.addRecipe(recipePart);
}
_lists.put(new Integer(_lists.size()), recipeList);
}
catch (Exception e)
{
LOGGER.warning("Exception in RecipeController.parseList() " + e);
}
}
public int getRecipesCount()
{
return _lists.size();
}
public L2RecipeList getRecipeList(int listId)
{
return _lists.get(listId);
}
public L2RecipeList getRecipeByItemId(int itemId)
{
for (int i = 0; i < _lists.size(); i++)
{
final L2RecipeList find = _lists.get(new Integer(i));
if (find.getRecipeId() == itemId)
{
return find;
}
}
return null;
}
public L2RecipeList getRecipeById(int recId)
{
for (int i = 0; i < _lists.size(); i++)
{
final L2RecipeList find = _lists.get(new Integer(i));
if (find.getId() == recId)
{
return find;
}
}
return null;
}
}

View File

@ -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.gameserver.datatables.csv;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.LineNumberReader;
import java.util.HashMap;
import java.util.Map;
import java.util.StringTokenizer;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.model.actor.instance.L2StaticObjectInstance;
public class StaticObjects
{
private static Logger LOGGER = Logger.getLogger(StaticObjects.class.getName());
private static StaticObjects _instance;
private final Map<Integer, L2StaticObjectInstance> _staticObjects;
public static StaticObjects getInstance()
{
if (_instance == null)
{
_instance = new StaticObjects();
}
return _instance;
}
public StaticObjects()
{
_staticObjects = new HashMap<>();
parseData();
LOGGER.info("StaticObject: Loaded " + _staticObjects.size() + " StaticObject Templates.");
}
private void parseData()
{
FileReader reader = null;
BufferedReader buff = null;
LineNumberReader lnr = null;
try
{
final File doorData = new File(Config.DATAPACK_ROOT, "data/csv/staticobjects.csv");
reader = new FileReader(doorData);
buff = new BufferedReader(reader);
lnr = new LineNumberReader(buff);
String line = null;
while ((line = lnr.readLine()) != null)
{
if ((line.trim().length() == 0) || line.startsWith("#"))
{
continue;
}
L2StaticObjectInstance obj = parse(line);
_staticObjects.put(obj.getStaticObjectId(), obj);
}
}
catch (FileNotFoundException e)
{
LOGGER.warning("staticobjects.csv is missing in data csv folder");
}
catch (Exception e)
{
LOGGER.warning("error while creating StaticObjects table " + e);
}
finally
{
if (lnr != null)
{
try
{
lnr.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (buff != null)
{
try
{
buff.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
if (reader != null)
{
try
{
reader.close();
}
catch (Exception e1)
{
e1.printStackTrace();
}
}
}
}
public static L2StaticObjectInstance parse(String line)
{
StringTokenizer st = new StringTokenizer(line, ";");
st.nextToken(); // Pass over static object name (not used in server)
final int id = Integer.parseInt(st.nextToken());
final int x = Integer.parseInt(st.nextToken());
final int y = Integer.parseInt(st.nextToken());
final int z = Integer.parseInt(st.nextToken());
final int type = Integer.parseInt(st.nextToken());
final String texture = st.nextToken();
final int map_x = Integer.parseInt(st.nextToken());
final int map_y = Integer.parseInt(st.nextToken());
final L2StaticObjectInstance obj = new L2StaticObjectInstance(IdFactory.getInstance().getNextId());
obj.setType(type);
obj.setStaticObjectId(id);
obj.setXYZ(x, y, z);
obj.setMap(texture, map_x, map_y);
obj.spawnMe();
return obj;
}
}

View File

@ -0,0 +1,134 @@
/*
* 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.gameserver.datatables.csv;
import java.io.File;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.model.L2SummonItem;
public class SummonItemsData
{
private static Logger LOGGER = Logger.getLogger(SummonItemsData.class.getName());
private final Map<Integer, L2SummonItem> _summonitems;
private static SummonItemsData _instance;
public static SummonItemsData getInstance()
{
if (_instance == null)
{
_instance = new SummonItemsData();
}
return _instance;
}
public SummonItemsData()
{
_summonitems = new HashMap<>();
Scanner s = null;
try
{
s = new Scanner(new File(Config.DATAPACK_ROOT + "/data/csv/summon_items.csv"));
int lineCount = 0;
while (s.hasNextLine())
{
lineCount++;
String line = s.nextLine();
if (line.startsWith("#"))
{
continue;
}
else if (line.equals(""))
{
continue;
}
final String[] lineSplit = line.split(";");
boolean ok = true;
int itemID = 0, npcID = 0;
byte summonType = 0;
try
{
itemID = Integer.parseInt(lineSplit[0]);
npcID = Integer.parseInt(lineSplit[1]);
summonType = Byte.parseByte(lineSplit[2]);
}
catch (Exception e)
{
LOGGER.info("Summon items data: Error in line " + lineCount + " -> incomplete/invalid data or wrong seperator!");
LOGGER.info(" " + line);
ok = false;
}
if (!ok)
{
continue;
}
L2SummonItem summonitem = new L2SummonItem(itemID, npcID, summonType);
_summonitems.put(itemID, summonitem);
}
}
catch (Exception e)
{
LOGGER.info("Summon items data: Can not find './data/csv/summon_items.csv'");
}
finally
{
if (s != null)
{
s.close();
}
}
LOGGER.info("Summon items data: Loaded " + _summonitems.size() + " summon items.");
}
public L2SummonItem getSummonItem(int itemId)
{
return _summonitems.get(itemId);
}
public int[] itemIDs()
{
final int size = _summonitems.size();
final int[] result = new int[size];
int i = 0;
for (L2SummonItem si : _summonitems.values())
{
result[i] = si.getItemId();
i++;
}
return result;
}
}

View File

@ -0,0 +1,239 @@
/*
* 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.gameserver.datatables.sql;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.datatables.AccessLevel;
/**
* @author FBIagent<br>
*/
public class AccessLevels
{
private static final Logger LOGGER = Logger.getLogger(AccessLevels.class.getName());
/**
* The one and only instance of this class, retriveable by getInstance()<br>
*/
private static AccessLevels _instance = null;
/**
* Reserved master access level<br>
*/
// public static final int _masterAccessLevelNum = Config.MASTERACCESS_LEVEL;
/**
* The master access level which can use everything<br>
*/
// L2EMU_EDIT - Rayan -
public AccessLevel _masterAccessLevel;/*
* = new AccessLevel(_masterAccessLevelNum, "Master Access", Config.MASTERACCESS_NAME_COLOR, Config.MASTERACCESS_TITLE_COLOR, true, true, true, true, true, true, true, true, true, true, true); //L2EMU_EDIT /** Reserved user access level<br>
*/
// public static final int _userAccessLevelNum = 0;
/**
* The user access level which can do no administrative tasks<br>
*/
// L2EMU_EDIT - Rayan -
public AccessLevel _userAccessLevel;/*
* = new AccessLevel(_userAccessLevelNum, "User", Integer.decode("0xFFFFFF"), Integer.decode("0xFFFFFF"), false, false, false, true, false, true, true, true, true, true, false); //L2EMU_EDIT /** Map of access levels defined in database<br>
*/
private final Map<Integer, AccessLevel> _accessLevels = new HashMap<>();
/**
* Loads the access levels from database<br>
*/
private AccessLevels()
{
_masterAccessLevel = new AccessLevel(Config.MASTERACCESS_LEVEL, "Master Access", Config.MASTERACCESS_NAME_COLOR, Config.MASTERACCESS_TITLE_COLOR, true, true, true, true, true, true, true, true, true, true, true);
_userAccessLevel = new AccessLevel(Config.USERACCESS_LEVEL, "User", Integer.decode("0xFFFFFF"), Integer.decode("0xFFFFFF"), false, false, false, true, false, true, true, true, true, true, false);
try (Connection con = DatabaseFactory.getInstance().getConnection())
{
final PreparedStatement stmt = con.prepareStatement("SELECT * FROM `access_levels` ORDER BY `accessLevel` DESC");
final ResultSet rset = stmt.executeQuery();
int accessLevel = 0;
String name = null;
int nameColor = 0;
int titleColor = 0;
boolean isGm = false;
boolean allowPeaceAttack = false;
boolean allowFixedRes = false;
boolean allowTransaction = false;
boolean allowAltG = false;
boolean giveDamage = false;
boolean takeAggro = false;
boolean gainExp = false;
// L2EMU_ADD
boolean useNameColor = true;
boolean useTitleColor = false;
boolean canDisableGmStatus = true;
// L2EMU_ADD
while (rset.next())
{
accessLevel = rset.getInt("accessLevel");
name = rset.getString("name");
if (accessLevel == Config.USERACCESS_LEVEL)
{
LOGGER.info("AccessLevels: Access level with name " + name + " is using reserved user access level " + Config.USERACCESS_LEVEL + ". Ignoring it...");
continue;
}
else if (accessLevel == Config.MASTERACCESS_LEVEL)
{
LOGGER.info("AccessLevels: Access level with name " + name + " is using reserved master access level " + Config.MASTERACCESS_LEVEL + ". Ignoring it...");
continue;
}
else if (accessLevel < 0)
{
LOGGER.info("AccessLevels: Access level with name " + name + " is using banned access level state(below 0). Ignoring it...");
continue;
}
try
{
nameColor = Integer.decode("0x" + rset.getString("nameColor"));
}
catch (NumberFormatException nfe)
{
LOGGER.warning(nfe.getMessage());
try
{
nameColor = Integer.decode("0xFFFFFF");
}
catch (NumberFormatException nfe2)
{
LOGGER.warning(nfe.getMessage());
}
}
try
{
titleColor = Integer.decode("0x" + rset.getString("titleColor"));
}
catch (NumberFormatException nfe)
{
LOGGER.warning(nfe.getMessage());
try
{
titleColor = Integer.decode("0x77FFFF");
}
catch (NumberFormatException nfe2)
{
LOGGER.warning(nfe.getMessage());
}
}
isGm = rset.getBoolean("isGm");
allowPeaceAttack = rset.getBoolean("allowPeaceAttack");
allowFixedRes = rset.getBoolean("allowFixedRes");
allowTransaction = rset.getBoolean("allowTransaction");
allowAltG = rset.getBoolean("allowAltg");
giveDamage = rset.getBoolean("giveDamage");
takeAggro = rset.getBoolean("takeAggro");
gainExp = rset.getBoolean("gainExp");
// L2EMU_ADD - Rayan for temp access
useNameColor = rset.getBoolean("useNameColor");
useTitleColor = rset.getBoolean("useTitleColor");
canDisableGmStatus = rset.getBoolean("canDisableGmStatus");
// L2EMU_EDIT - Rayan for temp access
_accessLevels.put(accessLevel, new AccessLevel(accessLevel, name, nameColor, titleColor, isGm, allowPeaceAttack, allowFixedRes, allowTransaction, allowAltG, giveDamage, takeAggro, gainExp, useNameColor, useTitleColor, canDisableGmStatus));
// L2EMU_EDIT
}
rset.close();
stmt.close();
}
catch (SQLException e)
{
LOGGER.warning("AccessLevels: Error loading from database " + e);
}
// LOGGER.info("AccessLevels: Loaded " + _accessLevels.size() + " Access Levels from database.");
LOGGER.info("AccessLevels: Master Access Level is " + Config.MASTERACCESS_LEVEL);
LOGGER.info("AccessLevels: User Access Level is " + Config.USERACCESS_LEVEL);
if (Config.DEBUG)
{
for (int actual : _accessLevels.keySet())
{
final AccessLevel actual_access = _accessLevels.get(actual);
LOGGER.info("AccessLevels: " + actual_access.getName() + " Access Level is " + actual_access.getLevel());
}
}
}
/**
* Returns the one and only instance of this class<br>
* <br>
* @return AccessLevels: the one and only instance of this class<br>
*/
public static AccessLevels getInstance()
{
return _instance == null ? (_instance = new AccessLevels()) : _instance;
}
/**
* Returns the access level by characterAccessLevel<br>
* <br>
* @param accessLevelNum as int<br>
* <br>
* @return AccessLevel: AccessLevel instance by char access level<br>
*/
public AccessLevel getAccessLevel(int accessLevelNum)
{
AccessLevel accessLevel = null;
synchronized (_accessLevels)
{
accessLevel = _accessLevels.get(accessLevelNum);
}
return accessLevel;
}
public void addBanAccessLevel(int accessLevel)
{
synchronized (_accessLevels)
{
if (accessLevel > -1)
{
return;
}
// L2EMU_ADD - Rayan -
_accessLevels.put(accessLevel, new AccessLevel(accessLevel, "Banned", Integer.decode("0x000000"), Integer.decode("0x000000"), false, false, false, false, false, false, false, false, false, false, false));
// L2EMU_ADD
}
}
public static void reload()
{
_instance = null;
getInstance();
}
}

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