Compare commits
37 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
799cb97564 | ||
|
|
851b57e8ef | ||
|
|
ceb2866aa1 | ||
|
|
11c1e4655e | ||
|
|
aca9cbf91d | ||
|
|
08b089d9be | ||
|
|
8b254a294e | ||
|
|
7004de6e71 | ||
|
|
738e1b24e6 | ||
|
|
5a4200cc8e | ||
|
|
cb0320a471 | ||
|
|
058f034c2b | ||
|
|
9c54f3a8ea | ||
|
|
ee8cb545e1 | ||
|
|
64bbff462d | ||
|
|
f63f7e13d4 | ||
|
|
db8666fc71 | ||
|
|
93ea66e6fe | ||
|
|
7131e39c96 | ||
|
|
b80e9a3310 | ||
|
|
03802666ef | ||
|
|
ecd155f2bb | ||
|
|
d6147d5191 | ||
|
|
7ef471f1e2 | ||
|
|
c3404d296a | ||
|
|
c609bcf2ee | ||
|
|
0aee9d7c3f | ||
|
|
a49c1703ae | ||
|
|
c145a53688 | ||
|
|
c744935dd0 | ||
|
|
90b44c3a8b | ||
|
|
60a44252ea | ||
|
|
38eecd0db7 | ||
|
|
e320bafa8b | ||
|
|
754e5e61f2 | ||
|
|
2d7525f2b4 | ||
|
|
7adb25888f |
@@ -234,6 +234,7 @@ server:
|
||||
USE_STARTING_AP_4: false #Use early-GMS 4/4/4/4 starting stats. To overcome AP shortage, this gives 4AP/5AP at 1st/2nd job advancements.
|
||||
USE_AUTOBAN: false #Commands the server to detect infractors automatically.
|
||||
USE_AUTOBAN_LOG: true #Log autoban related messages. Still logs even with USE_AUTOBAN disabled.
|
||||
USE_EXP_GAIN_LOG: false #Logs characters exp gains; logs world rate & coupon exp, total gained exp, and current exp, level can be calculated from "ExpTable".
|
||||
USE_AUTOSAVE: true #Enables server autosaving feature (saves characters to DB each 1 hour).
|
||||
USE_SERVER_AUTOASSIGNER: false #HeavenMS-builtin autoassigner, uses algorithm based on distributing AP accordingly with required secondary stat on equipments.
|
||||
USE_REFRESH_RANK_MOVE: true
|
||||
@@ -346,7 +347,6 @@ server:
|
||||
USE_PERFECT_SCROLLING: false #Scrolls doesn't use slots upon failure.
|
||||
USE_ENHANCED_CHSCROLL: false #Equips even more powerful with chaos upgrade.
|
||||
USE_ENHANCED_CRAFTING: false #Apply chaos scroll on every equip crafted.
|
||||
USE_ENHANCED_CLNSLATE: false #Clean slates can be applied to recover successfully used slots as well.
|
||||
SCROLL_CHANCE_ROLLS: 1 #Number of rolls for success on a scroll, set 1 for default.
|
||||
CHSCROLL_STAT_RATE: 1 #Number of rolls of stat upgrade on a successfully applied chaos scroll, set 1 for default.
|
||||
CHSCROLL_STAT_RANGE: 6 #Stat upgrade range (-N, N) on chaos scrolls.
|
||||
|
||||
@@ -21484,6 +21484,17 @@ CREATE TABLE IF NOT EXISTS `worldtransfers` (
|
||||
INDEX (characterid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `characterexplogs` (
|
||||
`id` BIGINT NOT NULL AUTO_INCREMENT,
|
||||
`world_exp_rate` INT,
|
||||
`exp_coupon` INT,
|
||||
`gained_exp` BIGINT,
|
||||
`current_exp` INT,
|
||||
`exp_gain_time` DATETIME,
|
||||
`charid` int(11) NOT NULL,
|
||||
PRIMARY KEY (`id`),
|
||||
FOREIGN KEY (`charid`) REFERENCES `characters`(`id`) ON DELETE CASCADE
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=1 ;
|
||||
|
||||
ALTER TABLE `dueyitems`
|
||||
ADD CONSTRAINT `dueyitems_ibfk_1` FOREIGN KEY (`PackageId`) REFERENCES `dueypackages` (`PackageId`) ON DELETE CASCADE;
|
||||
|
||||
@@ -31,7 +31,7 @@ function action(mode, type, selection) {
|
||||
}
|
||||
|
||||
if (status == 0) {
|
||||
cm.sendSimple("Hey, you look like you need a breather. You should be enjoying the life, just like I am. Well, if you have a couple of items, I can trade you for an item you can play minigames with. Now... what can I do for you?#b\r\n#L0#Create a minigame item#l\r\n#L1#Explain to me what the minigames are about#l#k");
|
||||
cm.sendSimple("Hey, you look like you need a breather from all that hunting. You should be enjoying the life, just like I am. Well, if you have a couple of items, I can trade you for an item you can play minigames with. Now... what can I do for you?#b\r\n#L0#Create a minigame item#l\r\n#L1#Explain to me what the minigames are about#l#k");
|
||||
|
||||
} else if (status == 1) {
|
||||
if (selection == 0) {
|
||||
@@ -57,6 +57,7 @@ function action(mode, type, selection) {
|
||||
if (cm.haveItem(4030012, 15)) {
|
||||
cm.gainItem(4030012, -15);
|
||||
cm.gainItem(4080100, 1);
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendNext("You want #bA set of Match Cards#k? Hmm...to make A set of Match Cards, you'll need some #bMonster Cards#k. Monster Card can be obtained by taking out the monsters all around the island. Collect 15 Monster Cards and you can make a set of A set of Match Cards."); //Lmfao a set of A set xD
|
||||
cm.dispose();
|
||||
@@ -81,7 +82,7 @@ function action(mode, type, selection) {
|
||||
if (current == 1 || current == 2) {
|
||||
cm.sendNextPrev("Enter the room, and when you're ready to play, click on #bReady#k.\r\nOnce the visitor clicks on #bReady#k, the room owner can press #bStart#k to begin the game. If an unwanted visitor walks in, and you don't want to play with that person, the room owner has the right to kick the visitor out of the room. There will be a square box with x written on the right of that person. Click on that for a cold goodbye, okay?"); //Oh yeah, because people WALK in Omok Rooms.
|
||||
} else if (current == 3) {
|
||||
if (cm.haveItem(omok1piece[selection], 99) && cm.haveItem(omok2piece[selection], 99) && cm.haveItem(4030009, 1)) {
|
||||
if (cm.haveItem(omok1piece[selection], omokamount) && cm.haveItem(omok2piece[selection], omokamount) && cm.haveItem(4030009, 1)) {
|
||||
cm.gainItem(omok1piece[selection], -omokamount);
|
||||
cm.gainItem(omok2piece[selection], -omokamount);
|
||||
cm.gainItem(4030009, -1);
|
||||
@@ -95,7 +96,7 @@ function action(mode, type, selection) {
|
||||
|
||||
} else if (status == 5) {
|
||||
if (current == 1) {
|
||||
cm.sendNextPrev("When the first fame starts, #bthe room owner goes first#k. Beward that you'll be given a time limit, and you may lose your turn if you don't make your move on time. Normally, 3 x 3 is not allowed, but if there comes a point that it's absolutely necessary to put your piece there or face ending the game, then you can put it there. 3 x 3 is allowed as the last line of defense! Oh, and it won't count if it's #r6 or 7 straight#k. Only 5!");
|
||||
cm.sendNextPrev("When the first game starts, #bthe room owner goes first#k. Beward that you'll be given a time limit, and you may lose your turn if you don't make your move on time. Normally, 3 x 3 is not allowed, but if there comes a point that it's absolutely necessary to put your piece there or face ending the game, then you can put it there. 3 x 3 is allowed as the last line of defense! Oh, and it won't count if it's #r6 or 7 straight#k. Only 5!");
|
||||
} else if (current == 2) {
|
||||
cm.sendNextPrev("Oh, and unlike Omok, when you create the game room for Match Cards, you'll need to set your game on the number of cards you'll use for the game. There are 3 modes avaliable, 3x4, 4x5, and 5x6, which will require 12, 20, and 30 cards respectively. Remember that you won't beable to change it up once the room is open, so if you really wish to change it up, you may have to close the room and open another one.");
|
||||
}
|
||||
@@ -110,12 +111,14 @@ function action(mode, type, selection) {
|
||||
} else if (status == 7) {
|
||||
if (current == 1) {
|
||||
cm.sendPrev("When the next game starts, the loser will go first. Also, no one is allowed to leave in the middle of a game. If you do, you may need to request either a #bforfeit or tie#k. (Of course, if you request a forfeit, you'll lose the game.) And if you click on 'Leave' in the middle of the game and call to leave after the game, you'll leave the room right after the game is over. This will be a much more useful way to leave.");
|
||||
cm.dispose();
|
||||
} else if (current == 2) {
|
||||
cm.sendNextPrev("If you and your opponent have the same number of matched pairs, then whoever had a longer streak of matched pairs will win. If you ever feel the need to go to the bathroom, or take an extended break, you can request a #btie#k. The game will end in a tie if the opponent accepts the request. Tip: this may be a good way to keep your friendships in tact.");
|
||||
}
|
||||
} else if (status == 8) {
|
||||
if (current == 2) {
|
||||
cm.sendPrev("When the next game starts, the loser will go first. Also, no one is allowed to leave in the middle of a game. If you do, you may need to request either a #bforfeit or tie#k. (Of course, if you request a forfeit, you'll lose the game.) And if you click on 'Leave' in the middle of the game and call to leave after the game, you'll leave the room right after the game is over. This will be a much more useful way to leave.");
|
||||
cm.dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -23,7 +23,12 @@ function action(mode, type, selection) {
|
||||
}
|
||||
|
||||
if (!cm.isEventLeader()) {
|
||||
cm.sendYesNo("I wish for your leader to talk to me. Alternatively, you may be wanting to quit. Are you going to abandon this campaign?");
|
||||
// Player chose "No" or "End Chat"
|
||||
if (mode <= 0) {
|
||||
cm.dispose();
|
||||
} else {
|
||||
cm.sendYesNo("I wish for your leader to talk to me. Alternatively, you may be wanting to quit. Are you going to abandon this campaign?");
|
||||
}
|
||||
} else {
|
||||
var eim = cm.getEventInstance();
|
||||
if (eim == null) {
|
||||
|
||||
@@ -59,6 +59,7 @@ import scripting.AbstractPlayerInteraction;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import scripting.item.ItemScriptManager;
|
||||
import server.*;
|
||||
import server.ExpLogger.ExpLogRecord;
|
||||
import server.ItemInformationProvider.ScriptedItem;
|
||||
import server.events.Events;
|
||||
import server.events.RescueGaga;
|
||||
@@ -150,6 +151,7 @@ public class Character extends AbstractCharacterObject {
|
||||
private final AtomicInteger gachaexp = new AtomicInteger();
|
||||
private final AtomicInteger meso = new AtomicInteger();
|
||||
private final AtomicInteger chair = new AtomicInteger(-1);
|
||||
private long totalExpGained = 0;
|
||||
private int merchantmeso;
|
||||
private BuddyList buddylist;
|
||||
private EventInstanceManager eventInstance = null;
|
||||
@@ -3092,6 +3094,7 @@ public class Character extends AbstractCharacterObject {
|
||||
leftover = nextExp - Integer.MAX_VALUE;
|
||||
}
|
||||
updateSingleStat(Stat.EXP, exp.addAndGet((int) total));
|
||||
totalExpGained += total;
|
||||
if (show) {
|
||||
announceExpGain(gain, equip, party, inChat, white);
|
||||
}
|
||||
@@ -3108,6 +3111,20 @@ public class Character extends AbstractCharacterObject {
|
||||
gainExpInternal(leftover, equip, party, false, inChat, white);
|
||||
} else {
|
||||
lastExpGainTime = System.currentTimeMillis();
|
||||
|
||||
if (YamlConfig.config.server.USE_EXP_GAIN_LOG) {
|
||||
ExpLogRecord expLogRecord = new ExpLogger.ExpLogRecord(
|
||||
getWorldServer().getExpRate(),
|
||||
expCoupon,
|
||||
totalExpGained,
|
||||
exp.get(),
|
||||
new Timestamp(lastExpGainTime),
|
||||
id
|
||||
);
|
||||
ExpLogger.putExpLogRecord(expLogRecord);
|
||||
}
|
||||
|
||||
totalExpGained = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,7 +39,6 @@ import net.server.Server;
|
||||
import net.server.channel.Channel;
|
||||
import net.server.coordinator.login.LoginBypassCoordinator;
|
||||
import net.server.coordinator.session.Hwid;
|
||||
import net.server.coordinator.session.IpAddresses;
|
||||
import net.server.coordinator.session.SessionCoordinator;
|
||||
import net.server.coordinator.session.SessionCoordinator.AntiMulticlientResult;
|
||||
import net.server.guild.Guild;
|
||||
@@ -176,10 +175,7 @@ public class Client extends ChannelInboundHandlerAdapter {
|
||||
private static String getRemoteAddress(io.netty.channel.Channel channel) {
|
||||
String remoteAddress = "null";
|
||||
try {
|
||||
String hostAddress = ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress();
|
||||
if (hostAddress != null) {
|
||||
remoteAddress = IpAddresses.evaluateRemoteAddress(hostAddress); // thanks dyz for noticing Local/LAN/WAN connections not interacting properly
|
||||
}
|
||||
remoteAddress = ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress();
|
||||
} catch (NullPointerException npe) {
|
||||
log.warn("Unable to get remote address for client", npe);
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public class KarmaManipulator {
|
||||
flag ^= karmaFlag;
|
||||
flag |= ItemConstants.UNTRADEABLE;
|
||||
|
||||
item.setFlag((byte) flag);
|
||||
item.setFlag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,6 +53,6 @@ public class KarmaManipulator {
|
||||
|
||||
flag |= karmaFlag;
|
||||
flag &= (0xFFFFFFFF ^ ItemConstants.UNTRADEABLE);
|
||||
item.setFlag((byte) flag);
|
||||
item.setFlag(flag);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,6 +82,7 @@ public class ServerConfig {
|
||||
public boolean USE_STARTING_AP_4;
|
||||
public boolean USE_AUTOBAN;
|
||||
public boolean USE_AUTOBAN_LOG;
|
||||
public boolean USE_EXP_GAIN_LOG;
|
||||
public boolean USE_AUTOSAVE;
|
||||
public boolean USE_SERVER_AUTOASSIGNER;
|
||||
public boolean USE_REFRESH_RANK_MOVE;
|
||||
@@ -194,7 +195,6 @@ public class ServerConfig {
|
||||
public boolean USE_PERFECT_SCROLLING;
|
||||
public boolean USE_ENHANCED_CHSCROLL;
|
||||
public boolean USE_ENHANCED_CRAFTING;
|
||||
public boolean USE_ENHANCED_CLNSLATE;
|
||||
public int SCROLL_CHANCE_ROLLS;
|
||||
public int CHSCROLL_STAT_RATE;
|
||||
public int CHSCROLL_STAT_RANGE;
|
||||
|
||||
@@ -15,7 +15,6 @@ import net.encryption.InitializationVector;
|
||||
import net.encryption.PacketCodec;
|
||||
import net.packet.logging.InPacketLogger;
|
||||
import net.packet.logging.OutPacketLogger;
|
||||
import net.server.coordinator.session.IpAddresses;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import tools.PacketCreator;
|
||||
@@ -35,10 +34,7 @@ public abstract class ServerChannelInitializer extends ChannelInitializer<Socket
|
||||
String getRemoteAddress(Channel channel) {
|
||||
String remoteAddress = "null";
|
||||
try {
|
||||
String hostAddress = ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress();
|
||||
if (hostAddress != null) {
|
||||
remoteAddress = IpAddresses.evaluateRemoteAddress(hostAddress); // thanks dyz for noticing Local/LAN/WAN connections not interacting properly
|
||||
}
|
||||
remoteAddress = ((InetSocketAddress) channel.remoteAddress()).getAddress().getHostAddress();
|
||||
} catch (NullPointerException npe) {
|
||||
log.warn("Unable to get remote address from netty Channel: {}", channel, npe);
|
||||
}
|
||||
|
||||
@@ -22,6 +22,8 @@ public class ByteBufInPacket implements InPacket {
|
||||
public byte readByte() {
|
||||
return byteBuf.readByte();
|
||||
}
|
||||
@Override
|
||||
public short readUnsignedByte() { return byteBuf.readUnsignedByte(); }
|
||||
|
||||
@Override
|
||||
public short readShort() {
|
||||
|
||||
@@ -4,6 +4,7 @@ import java.awt.*;
|
||||
|
||||
public interface InPacket extends Packet {
|
||||
byte readByte();
|
||||
short readUnsignedByte();
|
||||
short readShort();
|
||||
int readInt();
|
||||
long readLong();
|
||||
|
||||
@@ -57,7 +57,7 @@ import server.SkillbookInformationProvider;
|
||||
import server.ThreadManager;
|
||||
import server.TimerManager;
|
||||
import server.expeditions.ExpeditionBossLog;
|
||||
import server.life.PlayerNPCFactory;
|
||||
import server.life.PlayerNPC;
|
||||
import server.quest.Quest;
|
||||
import service.NoteService;
|
||||
import tools.DatabaseConnection;
|
||||
@@ -849,15 +849,15 @@ public class Server {
|
||||
final ExecutorService initExecutor = Executors.newFixedThreadPool(10);
|
||||
// Run slow operations asynchronously to make startup faster
|
||||
final List<Future<?>> futures = new ArrayList<>();
|
||||
futures.add(initExecutor.submit(() -> SkillFactory.loadAllSkills()));
|
||||
futures.add(initExecutor.submit(() -> CashItemFactory.loadAllCashItems()));
|
||||
futures.add(initExecutor.submit(() -> Quest.loadAllQuests()));
|
||||
futures.add(initExecutor.submit(() -> SkillbookInformationProvider.loadAllSkillbookInformation()));
|
||||
futures.add(initExecutor.submit(() -> PlayerNPCFactory.loadFactoryMetadata()));
|
||||
futures.add(initExecutor.submit(SkillFactory::loadAllSkills));
|
||||
futures.add(initExecutor.submit(CashItemFactory::loadAllCashItems));
|
||||
futures.add(initExecutor.submit(Quest::loadAllQuests));
|
||||
futures.add(initExecutor.submit(SkillbookInformationProvider::loadAllSkillbookInformation));
|
||||
initExecutor.shutdown();
|
||||
|
||||
TimeZone.setDefault(TimeZone.getTimeZone(YamlConfig.config.server.TIMEZONE));
|
||||
|
||||
final int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS);
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
setAllLoggedOut(con);
|
||||
setAllMerchantsInactive(con);
|
||||
@@ -868,6 +868,7 @@ public class Server {
|
||||
CashIdGenerator.loadExistentCashIdsFromDb(con);
|
||||
applyAllNameChanges(con); // -- name changes can be missed by INSTANT_NAME_CHANGE --
|
||||
applyAllWorldTransfers(con);
|
||||
PlayerNPC.loadRunningRankData(con, worldCount);
|
||||
} catch (SQLException sqle) {
|
||||
log.error("Failed to run all startup-bound database tasks", sqle);
|
||||
throw new IllegalStateException(sqle);
|
||||
@@ -877,8 +878,6 @@ public class Server {
|
||||
initializeTimelyTasks(channelDependencies); // aggregated method for timely tasks thanks to lxconan
|
||||
|
||||
try {
|
||||
int worldCount = Math.min(GameConstants.WORLD_NAMES.length, YamlConfig.config.server.WORLDS);
|
||||
|
||||
for (int i = 0; i < worldCount; i++) {
|
||||
initWorld();
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import constants.id.ItemId;
|
||||
import constants.id.MapId;
|
||||
import net.AbstractPacketHandler;
|
||||
import net.packet.InPacket;
|
||||
import net.server.Server;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import server.Trade;
|
||||
@@ -181,7 +182,11 @@ public final class ChangeMapHandler extends AbstractPacketHandler {
|
||||
c.disconnect(false, false);
|
||||
return;
|
||||
}
|
||||
String[] socket = c.getChannelServer().getIP().split(":");
|
||||
String[] socket = Server.getInstance().getInetSocket(c, c.getWorld(), c.getChannel());
|
||||
if (socket == null) {
|
||||
c.enableCSActions();
|
||||
return;
|
||||
}
|
||||
chr.getCashShop().open(false);
|
||||
|
||||
chr.setSessionTransitionState();
|
||||
|
||||
@@ -45,6 +45,8 @@ import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.List;
|
||||
@@ -122,40 +124,12 @@ public final class MTSHandler extends AbstractPacketHandler {
|
||||
return;
|
||||
}
|
||||
}
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
int year;
|
||||
int month;
|
||||
int day;
|
||||
int oldmax = calendar.getActualMaximum(Calendar.DAY_OF_MONTH);
|
||||
int oldday = calendar.get(Calendar.DAY_OF_MONTH) + 7;
|
||||
if (oldmax < oldday) {
|
||||
if (calendar.get(Calendar.MONTH) + 2 > 12) {
|
||||
year = calendar.get(Calendar.YEAR) + 1;
|
||||
month = 1;
|
||||
calendar.set(year, month, 1);
|
||||
day = oldday - oldmax;
|
||||
} else {
|
||||
month = calendar.get(Calendar.MONTH) + 2;
|
||||
year = calendar.get(Calendar.YEAR);
|
||||
calendar.set(year, month, 1);
|
||||
day = oldday - oldmax;
|
||||
}
|
||||
} else {
|
||||
day = calendar.get(Calendar.DAY_OF_MONTH) + 7;
|
||||
month = calendar.get(Calendar.MONTH);
|
||||
year = calendar.get(Calendar.YEAR);
|
||||
}
|
||||
String date = year + "-";
|
||||
if (month < 10) {
|
||||
date += "0" + month + "-";
|
||||
} else {
|
||||
date += month + "-";
|
||||
}
|
||||
if (day < 10) {
|
||||
date += "0" + day;
|
||||
} else {
|
||||
date += day + "";
|
||||
}
|
||||
|
||||
LocalDate now = LocalDate.now();
|
||||
LocalDate sellEnd = now.plusDays(7);
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
String date = sellEnd.format(formatter);
|
||||
|
||||
if (!i.getInventoryType().equals(InventoryType.EQUIP)) {
|
||||
Item item = i;
|
||||
try (PreparedStatement pse = con.prepareStatement("INSERT INTO mts_items (tab, type, itemid, quantity, expiration, giftFrom, seller, price, owner, sellername, sell_ends) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")) {
|
||||
@@ -761,7 +735,7 @@ public final class MTSHandler extends AbstractPacketHandler {
|
||||
}
|
||||
}
|
||||
}
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM mts_items WHERE tab = ? " + (type != 0 ? "AND type = ?" : "") + "AND transfer = 0")) {
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT COUNT(*) FROM mts_items WHERE tab = ? " + (type != 0 ? "AND type = ?" : "") + " AND transfer = 0")) {
|
||||
ps.setInt(1, tab);
|
||||
if (type != 0) {
|
||||
ps.setInt(2, type);
|
||||
|
||||
@@ -59,7 +59,7 @@ public final class NPCMoreTalkHandler extends AbstractPacketHandler {
|
||||
if (p.available() >= 4) {
|
||||
selection = p.readInt();
|
||||
} else if (p.available() > 0) {
|
||||
selection = p.readByte();
|
||||
selection = p.readUnsignedByte();
|
||||
}
|
||||
if (c.getQM() != null) {
|
||||
if (c.getQM().isStart()) {
|
||||
|
||||
@@ -25,8 +25,12 @@ import client.Character;
|
||||
import client.Client;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.inventory.*;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Equip.ScrollResult;
|
||||
import client.inventory.Inventory;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.ModifyInventory;
|
||||
import client.inventory.manipulator.InventoryManipulator;
|
||||
import constants.id.ItemId;
|
||||
import constants.inventory.ItemConstants;
|
||||
@@ -37,7 +41,6 @@ import tools.PacketCreator;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author Matze
|
||||
@@ -50,8 +53,8 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
if (c.tryacquireClient()) {
|
||||
try {
|
||||
p.readInt(); // whatever...
|
||||
short slot = p.readShort();
|
||||
short dst = p.readShort();
|
||||
short scrollSlot = p.readShort();
|
||||
short equipSlot = p.readShort();
|
||||
byte ws = (byte) p.readShort();
|
||||
boolean whiteScroll = false; // white scroll being used?
|
||||
boolean legendarySpirit = false; // legendary spirit skill
|
||||
@@ -61,24 +64,21 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
|
||||
ItemInformationProvider ii = ItemInformationProvider.getInstance();
|
||||
Character chr = c.getPlayer();
|
||||
Equip toScroll = (Equip) chr.getInventory(InventoryType.EQUIPPED).getItem(dst);
|
||||
Equip toScroll = (Equip) chr.getInventory(InventoryType.EQUIPPED).getItem(equipSlot);
|
||||
Skill LegendarySpirit = SkillFactory.getSkill(1003);
|
||||
if (chr.getSkillLevel(LegendarySpirit) > 0 && dst >= 0) {
|
||||
if (chr.getSkillLevel(LegendarySpirit) > 0 && equipSlot >= 0) {
|
||||
legendarySpirit = true;
|
||||
toScroll = (Equip) chr.getInventory(InventoryType.EQUIP).getItem(dst);
|
||||
toScroll = (Equip) chr.getInventory(InventoryType.EQUIP).getItem(equipSlot);
|
||||
}
|
||||
byte oldLevel = toScroll.getLevel();
|
||||
byte oldSlots = toScroll.getUpgradeSlots();
|
||||
Inventory useInventory = chr.getInventory(InventoryType.USE);
|
||||
Item scroll = useInventory.getItem(slot);
|
||||
Item scroll = useInventory.getItem(scrollSlot);
|
||||
Item wscroll = null;
|
||||
|
||||
if (ItemConstants.isCleanSlate(scroll.getItemId())) {
|
||||
Map<String, Integer> eqStats = ii.getEquipStats(toScroll.getItemId()); // clean slate issue found thanks to Masterrulax
|
||||
if (eqStats == null || eqStats.get("tuc") == 0) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
} else if (!ItemConstants.isModifierScroll(scroll.getItemId()) && toScroll.getUpgradeSlots() < 1) {
|
||||
announceCannotScroll(c, legendarySpirit); // thanks onechord for noticing zero upgrade slots freezing Legendary Scroll UI
|
||||
return;
|
||||
@@ -103,11 +103,6 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
}
|
||||
}
|
||||
|
||||
if (ItemConstants.isCleanSlate(scroll.getItemId()) && !ii.canUseCleanSlate(toScroll)) {
|
||||
announceCannotScroll(c, legendarySpirit);
|
||||
return;
|
||||
}
|
||||
|
||||
Equip scrolled = (Equip) ii.scrollEquipWithId(toScroll, scroll.getItemId(), whiteScroll, 0, chr.isGM());
|
||||
ScrollResult scrollSuccess = Equip.ScrollResult.FAIL; // fail
|
||||
if (scrolled == null) {
|
||||
@@ -141,7 +136,7 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
if (scrollSuccess == Equip.ScrollResult.CURSE) {
|
||||
if (!ItemId.isWeddingRing(toScroll.getItemId())) {
|
||||
mods.add(new ModifyInventory(3, toScroll));
|
||||
if (dst < 0) {
|
||||
if (equipSlot < 0) {
|
||||
Inventory inv = chr.getInventory(InventoryType.EQUIPPED);
|
||||
|
||||
inv.lockInventory();
|
||||
@@ -174,7 +169,7 @@ public final class ScrollHandler extends AbstractPacketHandler {
|
||||
}
|
||||
c.sendPacket(PacketCreator.modifyInventory(true, mods));
|
||||
chr.getMap().broadcastMessage(PacketCreator.getScrollEffect(chr.getId(), scrollSuccess, legendarySpirit, whiteScroll));
|
||||
if (dst < 0 && (scrollSuccess == Equip.ScrollResult.SUCCESS || scrollSuccess == Equip.ScrollResult.CURSE)) {
|
||||
if (equipSlot < 0 && (scrollSuccess == Equip.ScrollResult.SUCCESS || scrollSuccess == Equip.ScrollResult.CURSE)) {
|
||||
chr.equipChanged();
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -228,10 +228,10 @@ public final class UseCashItemHandler extends AbstractPacketHandler {
|
||||
return;
|
||||
}
|
||||
short flag = eq.getFlag();
|
||||
flag |= ItemConstants.LOCK;
|
||||
if (eq.getExpiration() > -1) {
|
||||
if (eq.getExpiration() > -1 && (eq.getFlag() & ItemConstants.LOCK) != ItemConstants.LOCK) {
|
||||
return; //No perma items pls
|
||||
}
|
||||
flag |= ItemConstants.LOCK;
|
||||
eq.setFlag(flag);
|
||||
|
||||
long period = 0;
|
||||
@@ -246,7 +246,8 @@ public final class UseCashItemHandler extends AbstractPacketHandler {
|
||||
}
|
||||
|
||||
if (period > 0) {
|
||||
eq.setExpiration(currentServerTime() + DAYS.toMillis(period));
|
||||
long expiration = eq.getExpiration() > -1 ? eq.getExpiration() : currentServerTime();
|
||||
eq.setExpiration(expiration + DAYS.toMillis(period));
|
||||
}
|
||||
|
||||
// double-remove found thanks to BHB
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package net.server.coordinator.session;
|
||||
|
||||
import config.YamlConfig;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
@@ -16,14 +14,6 @@ public class IpAddresses {
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public static String evaluateRemoteAddress(String inetAddress) {
|
||||
if (isLocalAddress(inetAddress) || isLanAddress(inetAddress)) {
|
||||
return YamlConfig.config.server.HOST;
|
||||
} else {
|
||||
return inetAddress;
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isLocalAddress(String inetAddress) {
|
||||
return inetAddress.startsWith("127.");
|
||||
}
|
||||
|
||||
@@ -291,7 +291,7 @@ public class AbstractPlayerInteraction {
|
||||
int size = Math.min(itemids.size(), quantity.size());
|
||||
|
||||
List<List<Pair<Integer, Integer>>> invList = new ArrayList<>(6);
|
||||
for (int i = InventoryType.UNDEFINED.getType(); i < InventoryType.CASH.getType(); i++) {
|
||||
for (int i = InventoryType.UNDEFINED.getType(); i <= InventoryType.CASH.getType(); i++) {
|
||||
invList.add(new LinkedList<>());
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,7 @@ public class QuestScriptManager extends AbstractScriptManager {
|
||||
|
||||
public void end(Client c, short questid, int npc) {
|
||||
Quest quest = Quest.getInstance(questid);
|
||||
if (!c.getPlayer().getQuest(quest).getStatus().equals(QuestStatus.Status.STARTED) || !c.getPlayer().getMap().containsNPC(npc)) {
|
||||
if (!c.getPlayer().getQuest(quest).getStatus().equals(QuestStatus.Status.STARTED) || (!c.getPlayer().getMap().containsNPC(npc) && !quest.isAutoComplete())) {
|
||||
dispose(c);
|
||||
return;
|
||||
}
|
||||
|
||||
93
src/main/java/server/ExpLogger.java
Normal file
93
src/main/java/server/ExpLogger.java
Normal file
@@ -0,0 +1,93 @@
|
||||
package server;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ThreadFactory;
|
||||
import java.sql.Timestamp;
|
||||
import static java.util.concurrent.TimeUnit.*;
|
||||
|
||||
import config.YamlConfig;
|
||||
import tools.DatabaseConnection;
|
||||
|
||||
public class ExpLogger {
|
||||
private static final LinkedBlockingQueue<ExpLogRecord> expLoggerQueue = new LinkedBlockingQueue<>();
|
||||
private static final short EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS = 60;
|
||||
private static final short EXP_LOGGER_THREAD_SHUTDOWN_WAIT_DURATION_MINUTES = 5;
|
||||
|
||||
public record ExpLogRecord(int worldExpRate, int expCoupon, long gainedExp, int currentExp,Timestamp expGainTime, int charid) {}
|
||||
|
||||
public static void putExpLogRecord(ExpLogRecord expLogRecord) {
|
||||
try {
|
||||
expLoggerQueue.put(expLogRecord);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
static private ScheduledExecutorService schdExctr = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
|
||||
@Override
|
||||
public Thread newThread(Runnable r) {
|
||||
Thread t = new Thread(r);
|
||||
t.setPriority(Thread.MIN_PRIORITY);
|
||||
return t;
|
||||
}
|
||||
});
|
||||
|
||||
private static Runnable saveExpLoggerToDBRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try (Connection con = DatabaseConnection.getConnection();
|
||||
PreparedStatement ps = con.prepareStatement("INSERT INTO characterexplogs (world_exp_rate, exp_coupon, gained_exp, current_exp, exp_gain_time, charid) VALUES (?, ?, ?, ?, ?, ?)")) {
|
||||
|
||||
List<ExpLogRecord> drainedExpLogs = new ArrayList<>();
|
||||
expLoggerQueue.drainTo(drainedExpLogs);
|
||||
for (ExpLogRecord expLogRecord : drainedExpLogs) {
|
||||
ps.setInt(1, expLogRecord.worldExpRate);
|
||||
ps.setInt(2, expLogRecord.expCoupon);
|
||||
ps.setLong(3, expLogRecord.gainedExp);
|
||||
ps.setInt(4, expLogRecord.currentExp);
|
||||
ps.setTimestamp(5, expLogRecord.expGainTime);
|
||||
ps.setInt(6, expLogRecord.charid);
|
||||
ps.addBatch();
|
||||
}
|
||||
ps.executeBatch();
|
||||
} catch (SQLException sqle) {
|
||||
sqle.printStackTrace();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
private static void startExpLogger() {
|
||||
schdExctr.scheduleWithFixedDelay(saveExpLoggerToDBRunnable, EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS, EXP_LOGGER_THREAD_SLEEP_DURATION_SECONDS, SECONDS);
|
||||
Runtime.getRuntime().addShutdownHook(new Thread(() -> {
|
||||
stopExpLogger();
|
||||
}));
|
||||
}
|
||||
|
||||
private static boolean stopExpLogger() {
|
||||
schdExctr.shutdown();
|
||||
try {
|
||||
schdExctr.awaitTermination(EXP_LOGGER_THREAD_SHUTDOWN_WAIT_DURATION_MINUTES, MINUTES);
|
||||
Thread runThreadBeforeShutdown = new Thread(saveExpLoggerToDBRunnable);
|
||||
runThreadBeforeShutdown.setPriority(Thread.MIN_PRIORITY);
|
||||
runThreadBeforeShutdown.start();
|
||||
return true;
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static {
|
||||
if (YamlConfig.config.server.USE_EXP_GAIN_LOG) {
|
||||
startExpLogger();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -22,9 +22,16 @@
|
||||
package server;
|
||||
|
||||
import client.Character;
|
||||
import client.*;
|
||||
import client.Client;
|
||||
import client.Job;
|
||||
import client.Skill;
|
||||
import client.SkillFactory;
|
||||
import client.autoban.AutobanFactory;
|
||||
import client.inventory.*;
|
||||
import client.inventory.Equip;
|
||||
import client.inventory.Inventory;
|
||||
import client.inventory.InventoryType;
|
||||
import client.inventory.Item;
|
||||
import client.inventory.WeaponType;
|
||||
import config.YamlConfig;
|
||||
import constants.id.ItemId;
|
||||
import constants.inventory.EquipSlot;
|
||||
@@ -35,19 +42,36 @@ import constants.skills.NightWalker;
|
||||
import net.server.Server;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import provider.*;
|
||||
import provider.Data;
|
||||
import provider.DataDirectoryEntry;
|
||||
import provider.DataFileEntry;
|
||||
import provider.DataProvider;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import server.MakerItemFactory.MakerItemCreateEntry;
|
||||
import server.life.LifeFactory;
|
||||
import server.life.MonsterInformationProvider;
|
||||
import tools.*;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.PacketCreator;
|
||||
import tools.Pair;
|
||||
import tools.Randomizer;
|
||||
import tools.StringUtil;
|
||||
|
||||
import java.sql.Connection;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.util.*;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author Matze
|
||||
@@ -204,13 +228,13 @@ public class ItemInformationProvider {
|
||||
} else if (itemId >= 1040000 && itemId < 1050000) {
|
||||
theData = eqpStringData;
|
||||
cat = "Eqp/Coat";
|
||||
} else if (itemId >= 20000 && itemId < 22000) {
|
||||
} else if (ItemConstants.isFace(itemId)) {
|
||||
theData = eqpStringData;
|
||||
cat = "Eqp/Face";
|
||||
} else if (itemId >= 1080000 && itemId < 1090000) {
|
||||
theData = eqpStringData;
|
||||
cat = "Eqp/Glove";
|
||||
} else if (itemId >= 30000 && itemId < 35000) {
|
||||
} else if (ItemConstants.isHair(itemId)) {
|
||||
theData = eqpStringData;
|
||||
cat = "Eqp/Hair";
|
||||
} else if (itemId >= 1050000 && itemId < 1060000) {
|
||||
@@ -1025,9 +1049,16 @@ public class ItemInformationProvider {
|
||||
Issue with clean slate found thanks to Masterrulax
|
||||
Vicious added in the clean slate check thanks to Crypter (CrypterDEV)
|
||||
*/
|
||||
public boolean canUseCleanSlate(Equip nEquip) {
|
||||
Map<String, Integer> eqstats = this.getEquipStats(nEquip.getItemId());
|
||||
return YamlConfig.config.server.USE_ENHANCED_CLNSLATE || nEquip.getUpgradeSlots() < (byte) (eqstats.get("tuc") + nEquip.getVicious());
|
||||
public boolean canUseCleanSlate(Equip equip) {
|
||||
Map<String, Integer> eqStats = getEquipStats(equip.getItemId());
|
||||
if (eqStats == null || eqStats.get("tuc") == 0 ) {
|
||||
return false;
|
||||
}
|
||||
int totalUpgradeCount = eqStats.get("tuc");
|
||||
int freeUpgradeCount = equip.getUpgradeSlots();
|
||||
int viciousCount = equip.getVicious();
|
||||
int appliedScrollCount = equip.getLevel();
|
||||
return freeUpgradeCount + appliedScrollCount < totalUpgradeCount + viciousCount;
|
||||
}
|
||||
|
||||
public Item scrollEquipWithId(Item equip, int scrollId, boolean usingWhiteScroll, int vegaItemId, boolean isGM) {
|
||||
|
||||
@@ -23,6 +23,8 @@ package server;
|
||||
|
||||
import client.inventory.Item;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.Calendar;
|
||||
|
||||
/**
|
||||
@@ -38,13 +40,16 @@ public class MTSItemInfo {
|
||||
private int day = 1;
|
||||
|
||||
public MTSItemInfo(Item item, int price, int id, int cid, String seller, String date) {
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
LocalDate sellEnd = LocalDate.parse(date, formatter);
|
||||
|
||||
this.item = item;
|
||||
this.price = price;
|
||||
this.seller = seller;
|
||||
this.id = id;
|
||||
this.year = Integer.parseInt(date.substring(0, 4));
|
||||
this.month = Integer.parseInt(date.substring(5, 7));
|
||||
this.day = Integer.parseInt(date.substring(8, 10));
|
||||
this.year = sellEnd.getYear();
|
||||
this.month = sellEnd.getMonthValue();
|
||||
this.day = sellEnd.getDayOfMonth();
|
||||
}
|
||||
|
||||
public Item getItem() {
|
||||
|
||||
@@ -53,6 +53,8 @@ import java.util.concurrent.atomic.AtomicInteger;
|
||||
* @author XoticStory
|
||||
* @author Ronan
|
||||
*/
|
||||
// TODO: remove dependency on custom Npc.wz. All NPCs with id 9901910 and above are custom additions for player npcs.
|
||||
// In summary: NPCs 9901910-9906599 and 9977777 are custom additions to HeavenMS that should be removed.
|
||||
public class PlayerNPC extends AbstractMapObject {
|
||||
private static final Logger log = LoggerFactory.getLogger(PlayerNPC.class);
|
||||
private static final Map<Byte, List<Integer>> availablePlayerNpcScriptIds = new HashMap<>();
|
||||
@@ -67,10 +69,6 @@ public class PlayerNPC extends AbstractMapObject {
|
||||
private int dir, FH, RX0, RX1, CY;
|
||||
private int worldRank, overallRank, worldJobRank, overallJobRank;
|
||||
|
||||
static {
|
||||
getRunningMetadata();
|
||||
}
|
||||
|
||||
public PlayerNPC(String name, int scriptId, int face, int hair, int gender, byte skin, Map<Short, Integer> equips, int dir, int FH, int RX0, int RX1, int CX, int CY, int oid) {
|
||||
this.equips = equips;
|
||||
this.scriptId = scriptId;
|
||||
@@ -128,6 +126,12 @@ public class PlayerNPC extends AbstractMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
public static void loadRunningRankData(Connection con, int worlds) throws SQLException {
|
||||
getRunningOverallRanks(con);
|
||||
getRunningWorldRanks(con, worlds);
|
||||
getRunningWorldJobRanks(con);
|
||||
}
|
||||
|
||||
public Map<Short, Integer> getEquips() {
|
||||
return equips;
|
||||
}
|
||||
@@ -213,16 +217,6 @@ public class PlayerNPC extends AbstractMapObject {
|
||||
client.sendPacket(PacketCreator.removePlayerNPC(this.getObjectId()));
|
||||
}
|
||||
|
||||
private static void getRunningMetadata() {
|
||||
try (Connection con = DatabaseConnection.getConnection()) {
|
||||
getRunningOverallRanks(con);
|
||||
getRunningWorldRanks(con);
|
||||
getRunningWorldJobRanks(con);
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static void getRunningOverallRanks(Connection con) throws SQLException {
|
||||
try (PreparedStatement ps = con.prepareStatement("SELECT max(overallrank) FROM playernpcs");
|
||||
ResultSet rs = ps.executeQuery()) {
|
||||
@@ -235,9 +229,8 @@ public class PlayerNPC extends AbstractMapObject {
|
||||
}
|
||||
}
|
||||
|
||||
private static void getRunningWorldRanks(Connection con) throws SQLException {
|
||||
int numWorlds = Server.getInstance().getWorldsSize();
|
||||
for (int i = 0; i < numWorlds; i++) {
|
||||
private static void getRunningWorldRanks(Connection con, int worlds) throws SQLException {
|
||||
for (int i = 0; i < worlds; i++) {
|
||||
runningWorldRank.add(new AtomicInteger(1));
|
||||
}
|
||||
|
||||
@@ -246,7 +239,7 @@ public class PlayerNPC extends AbstractMapObject {
|
||||
|
||||
while (rs.next()) {
|
||||
int wid = rs.getInt(1);
|
||||
if (wid < numWorlds) {
|
||||
if (wid < worlds) {
|
||||
runningWorldRank.get(wid).set(rs.getInt(2) + 1);
|
||||
}
|
||||
}
|
||||
@@ -649,4 +642,4 @@ public class PlayerNPC extends AbstractMapObject {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,124 +19,17 @@
|
||||
*/
|
||||
package server.life;
|
||||
|
||||
import constants.id.ItemId;
|
||||
import constants.id.MapId;
|
||||
import constants.id.NpcId;
|
||||
import net.server.Server;
|
||||
import provider.Data;
|
||||
import provider.DataProvider;
|
||||
import provider.DataProviderFactory;
|
||||
import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* @author RonanLana
|
||||
*/
|
||||
public class PlayerNPCFactory {
|
||||
private static final DataProvider npcData = DataProviderFactory.getDataProvider(WZFiles.NPC);
|
||||
|
||||
private static final Map<Integer, List<PlayerNPC>> dnpcMaps = new HashMap<>();
|
||||
private static Integer runningDeveloperOid = 2147483000; // 647 slots, long enough
|
||||
|
||||
public synchronized static boolean isExistentScriptid(int scriptid) {
|
||||
return npcData.getData(scriptid + ".img") != null;
|
||||
}
|
||||
|
||||
private static void loadDeveloperRoomMetadata(DataProvider npc) {
|
||||
Data thisData = npc.getData(NpcId.CUSTOM_DEV + ".img");
|
||||
if (thisData != null) {
|
||||
DataProvider map = DataProviderFactory.getDataProvider(WZFiles.MAP);
|
||||
|
||||
thisData = map.getData("Map/Map7/" + MapId.DEVELOPERS_HQ + ".img");
|
||||
if (thisData != null) {
|
||||
DataProvider sound = DataProviderFactory.getDataProvider(WZFiles.SOUND);
|
||||
|
||||
thisData = sound.getData("Field.img");
|
||||
if (thisData != null) {
|
||||
Data md = thisData.getChildByPath("anthem/brazil");
|
||||
if (md != null) {
|
||||
Server.getInstance().setAvailableDeveloperRoom();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static void loadFactoryMetadata() {
|
||||
DataProvider npc = npcData;
|
||||
loadDeveloperRoomMetadata(npc);
|
||||
|
||||
DataProvider etc = DataProviderFactory.getDataProvider(WZFiles.ETC);
|
||||
Data dnpcData = etc.getData("DeveloperNpc.img");
|
||||
if (dnpcData != null) {
|
||||
for (Data data : dnpcData.getChildren()) {
|
||||
int scriptId = Integer.parseInt(data.getName());
|
||||
|
||||
String name = DataTool.getString("name", data, "");
|
||||
int face = DataTool.getIntConvert("face", data, 20000);
|
||||
int hair = DataTool.getIntConvert("hair", data, 30000);
|
||||
int gender = DataTool.getIntConvert("gender", data, 0);
|
||||
byte skin = (byte) DataTool.getIntConvert("skin", data, 0);
|
||||
int dir = DataTool.getIntConvert("dir", data, 0);
|
||||
int mapid = DataTool.getIntConvert("map", data, 0);
|
||||
int FH = DataTool.getIntConvert("fh", data, 0);
|
||||
int RX0 = DataTool.getIntConvert("rx0", data, 0);
|
||||
int RX1 = DataTool.getIntConvert("rx1", data, 0);
|
||||
int CX = DataTool.getIntConvert("cx", data, 0);
|
||||
int CY = DataTool.getIntConvert("cy", data, 0);
|
||||
|
||||
Map<Short, Integer> equips = new HashMap<>();
|
||||
for (Data edata : data.getChildByPath("equips").getChildren()) {
|
||||
short equippos = (short) DataTool.getIntConvert("pos", edata);
|
||||
int equipid = DataTool.getIntConvert("itemid", edata);
|
||||
|
||||
equips.put(equippos, equipid);
|
||||
}
|
||||
|
||||
List<PlayerNPC> dnpcSet = dnpcMaps.get(mapid);
|
||||
if (dnpcSet == null) {
|
||||
dnpcSet = new LinkedList<>();
|
||||
dnpcMaps.put(mapid, dnpcSet);
|
||||
}
|
||||
|
||||
dnpcSet.add(new PlayerNPC(name, scriptId, face, hair, gender, skin, equips, dir, FH, RX0, RX1, CX, CY, runningDeveloperOid));
|
||||
runningDeveloperOid++;
|
||||
}
|
||||
} else {
|
||||
Data thisData = npc.getData(NpcId.CUSTOM_DEV + ".img");
|
||||
|
||||
if (thisData != null) {
|
||||
byte[] encData = {0x52, 0x6F, 0x6E, 0x61, 0x6E};
|
||||
String name = new String(encData);
|
||||
int face = 20104, hair = 30215, gender = 0, skin = 0, dir = 0, mapid = MapId.DEVELOPERS_HQ;
|
||||
int FH = 4, RX0 = -143, RX1 = -243, CX = -193, CY = 117, scriptId = NpcId.CUSTOM_DEV;
|
||||
|
||||
Map<Short, Integer> equips = new HashMap<>();
|
||||
equips.put((short) -1, ItemId.GREEN_HEADBAND);
|
||||
equips.put((short) -11, ItemId.TIMELESS_NIBLEHEIM);
|
||||
equips.put((short) -8, ItemId.BLUE_KORBEN);
|
||||
equips.put((short) -6, ItemId.MITHRIL_PLATINE_PANTS);
|
||||
equips.put((short) -7, ItemId.BLUE_CARZEN_BOOTS);
|
||||
equips.put((short) -5, ItemId.MITHRIL_PLATINE);
|
||||
|
||||
List<PlayerNPC> dnpcSet = dnpcMaps.get(mapid);
|
||||
if (dnpcSet == null) {
|
||||
dnpcSet = new LinkedList<>();
|
||||
dnpcMaps.put(mapid, dnpcSet);
|
||||
}
|
||||
|
||||
dnpcSet.add(new PlayerNPC(name, scriptId, face, hair, gender, (byte) skin, equips, dir, FH, RX0, RX1, CX, CY, runningDeveloperOid));
|
||||
runningDeveloperOid++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized static List<PlayerNPC> getDeveloperNpcsFromMapid(int mapid) {
|
||||
return dnpcMaps.get(mapid);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +28,10 @@ import provider.DataProviderFactory;
|
||||
import provider.DataTool;
|
||||
import provider.wz.WZFiles;
|
||||
import scripting.event.EventInstanceManager;
|
||||
import server.life.*;
|
||||
import server.life.AbstractLoadedLife;
|
||||
import server.life.LifeFactory;
|
||||
import server.life.Monster;
|
||||
import server.life.PlayerNPC;
|
||||
import server.partyquest.GuardianSpawnPoint;
|
||||
import tools.DatabaseConnection;
|
||||
import tools.StringUtil;
|
||||
@@ -249,13 +252,6 @@ public class MapFactory {
|
||||
} catch (SQLException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
List<PlayerNPC> dnpcs = PlayerNPCFactory.getDeveloperNpcsFromMapid(mapid);
|
||||
if (dnpcs != null) {
|
||||
for (PlayerNPC dnpc : dnpcs) {
|
||||
map.addPlayerNPCMapObject(dnpc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
loadLifeFromWz(map, mapData);
|
||||
|
||||
@@ -38,6 +38,36 @@ class ByteBufInPacketTest {
|
||||
assertEquals(writtenByte, readByte);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readUnsignedByte() {
|
||||
final byte writtenByte = Byte.MAX_VALUE;
|
||||
byteBuf.writeByte(writtenByte);
|
||||
|
||||
short readUnsignedByte = inPacket.readUnsignedByte();
|
||||
|
||||
assertEquals(writtenByte, readUnsignedByte);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readUnsignedByte_shouldBeNonnegative() {
|
||||
final byte writtenByte = Byte.MIN_VALUE;
|
||||
byteBuf.writeByte(writtenByte);
|
||||
|
||||
short readUnsignedByte = inPacket.readUnsignedByte();
|
||||
|
||||
assertEquals((short)writtenByte + 256, readUnsignedByte);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readUnsignedByte_shouldBeNonnegative2() {
|
||||
final byte writtenByte = -1;
|
||||
byteBuf.writeByte(writtenByte);
|
||||
|
||||
short readUnsignedByte = inPacket.readUnsignedByte();
|
||||
|
||||
assertEquals((short)writtenByte + 256, readUnsignedByte);
|
||||
}
|
||||
|
||||
@Test
|
||||
void readShort() {
|
||||
final short writtenShort = 12_345;
|
||||
|
||||
Reference in New Issue
Block a user